package projectmetadata

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/golang/protobuf/proto"
	xnetcontext "golang.org/x/net/context"
	"google.golang.org/grpc/codes"

	pb "code.justin.tv/dta/rockpaperscissors/proto"
)

var (
	// ErrOrgNameNotSet is returned when org_name isn't given when
	// requesting org details.
	ErrOrgNameNotSet = errf(codes.InvalidArgument, "No org_name was given.")
	// ErrOrgNotFound if returned when the given org name isn't in the datastore.
	ErrOrgNotFound = errf(codes.NotFound, "Org not found.")
)

// ListOrgs returns a list of org names, teams, and projects for each org.
func (s *Server) ListOrgs(ctx xnetcontext.Context, req *pb.ListOrgsRequest) (*pb.ListOrgsResponse, error) {
	records, err := s.listProjectRecords(req.IncludeArchived, nil, nil)
	if err != nil {
		return nil, err
	}

	orgsMap := make(map[string]map[string][]*pb.ProjectMetadata)

	// Fill in the static data
	for orgName, teams := range StaticHeirarchy {
		orgsMap[orgName] = make(map[string][]*pb.ProjectMetadata)
		for _, teamName := range teams {
			orgsMap[orgName][teamName] = make([]*pb.ProjectMetadata, 0)
		}
	}

	for _, record := range records {
		// TODO: check project_id and project_name attributes exist for each item?
		if len(record.OrgName) == 0 || len(record.TeamName) == 0 {
			continue
		}
		if orgsMap[record.OrgName] == nil {
			orgsMap[record.OrgName] = make(map[string][]*pb.ProjectMetadata)
		}
		orgsMap[record.OrgName][record.TeamName] = append(
			orgsMap[record.OrgName][record.TeamName], &pb.ProjectMetadata{
				ProjectId:   proto.String(record.ProjectID),
				ProjectName: proto.String(record.ProjectName),
				TeamName:    proto.String(record.TeamName),
				OrgName:     proto.String(record.OrgName),
				Archived:    proto.Bool(record.Archived),
			})
	}

	listResponse := &pb.ListOrgsResponse{}
	for orgName, teamsMap := range orgsMap {
		orgEntry := &pb.ListOrgsResponse_OrgEntry{
			OrgName: orgName,
		}
		listResponse.Orgs = append(listResponse.Orgs, orgEntry)
		for teamName, projects := range teamsMap {
			teamEntry := &pb.ListOrgsResponse_OrgEntry_TeamEntry{
				TeamName: teamName,
			}
			orgEntry.Teams = append(orgEntry.Teams, teamEntry)
			if req.IncludeProjects {
				teamEntry.Projects = projects
			}
		}
	}

	return listResponse, nil
}

// GetOrg returns some details about the org including what teams and projects are part of it.
func (s *Server) GetOrg(ctx xnetcontext.Context, req *pb.GetOrgRequest) (*pb.GetOrgResponse, error) {
	if len(req.OrgName) == 0 {
		return nil, ErrOrgNameNotSet
	}

	orgName := req.OrgName
	records, err := s.listProjectRecords(true, nil, &orgName)
	if err != nil {
		return nil, err
	}

	if len(records) == 0 {
		// No projects have that org, check static data before we say not found.
		if _, ok := StaticHeirarchy[orgName]; !ok {
			return nil, ErrOrgNotFound
		}
	}

	teamsMap := make(map[string][]*pb.ProjectMetadata)

	// Fill in the static data
	for _, teamName := range StaticHeirarchy[orgName] {
		teamsMap[teamName] = make([]*pb.ProjectMetadata, 0)
	}

	for _, record := range records {
		if len(record.TeamName) == 0 {
			continue
		}
		teamsMap[record.TeamName] = append(
			teamsMap[record.TeamName], &pb.ProjectMetadata{
				ProjectId:   aws.String(record.ProjectID),
				ProjectName: aws.String(record.ProjectName),
				OrgName:     aws.String(record.OrgName),
				Archived:    aws.Bool(record.Archived),
			})
	}

	response := &pb.GetOrgResponse{
		OrgName: orgName,
	}
	for teamName, projects := range teamsMap {
		teamEntry := &pb.GetOrgResponse_TeamEntry{
			TeamName: teamName,
		}
		response.Teams = append(response.Teams, teamEntry)
		if req.IncludeProjects {
			teamEntry.Projects = projects
		}
	}

	return response, nil
}
