package directory

import (
	"context"

	depmodel "security-center-backend/internal/modules/department/model"
	departmentrepo "security-center-backend/internal/modules/department/repository"
)

type RepositoryAdapter struct {
	employees   departmentrepo.EmployeeRepository
	departments departmentrepo.DepartmentRepository
}

func NewRepositoryAdapter(
	employees departmentrepo.EmployeeRepository,
	departments departmentrepo.DepartmentRepository,
) *RepositoryAdapter {
	return &RepositoryAdapter{
		employees:   employees,
		departments: departments,
	}
}

func (a *RepositoryAdapter) FindByID(ctx context.Context, id uint64) (*Employee, error) {
	if a == nil || a.employees == nil {
		return nil, nil
	}
	employee, err := a.employees.FindByID(ctx, id)
	if err != nil || employee == nil {
		return nil, err
	}
	item := a.enrichEmployee(ctx, toEmployee(*employee))
	return &item, nil
}

func (a *RepositoryAdapter) FindByEmail(ctx context.Context, email string) (*Employee, error) {
	if a == nil || a.employees == nil {
		return nil, nil
	}
	employee, err := a.employees.FindByEmail(ctx, email)
	if err != nil || employee == nil {
		return nil, err
	}
	item := a.enrichEmployee(ctx, toEmployee(*employee))
	return &item, nil
}

func (a *RepositoryAdapter) FindByEmails(ctx context.Context, emails []string) ([]Employee, error) {
	if a == nil || a.employees == nil {
		return []Employee{}, nil
	}
	employees, err := a.employees.FindByEmails(ctx, emails)
	if err != nil {
		return nil, err
	}
	active := make([]depmodel.Employee, 0, len(employees))
	for _, employee := range employees {
		if employee.Status == "active" {
			active = append(active, employee)
		}
	}
	return a.enrichEmployees(ctx, toEmployees(active)), nil
}

func (a *RepositoryAdapter) SearchEmployees(ctx context.Context, query string) ([]Employee, error) {
	if a == nil || a.employees == nil {
		return []Employee{}, nil
	}
	employees, err := a.employees.Search(ctx, query)
	if err != nil {
		return nil, err
	}
	return a.enrichEmployees(ctx, toEmployees(employees)), nil
}

func (a *RepositoryAdapter) SearchActiveEmployees(ctx context.Context, query string) ([]Employee, error) {
	if a == nil || a.employees == nil {
		return []Employee{}, nil
	}
	type activeSearcher interface {
		SearchActive(ctx context.Context, query string) ([]depmodel.Employee, error)
	}
	if searcher, ok := a.employees.(activeSearcher); ok {
		employees, err := searcher.SearchActive(ctx, query)
		if err != nil {
			return nil, err
		}
		return a.enrichEmployees(ctx, toEmployees(employees)), nil
	}
	employees, err := a.employees.Search(ctx, query)
	if err != nil {
		return nil, err
	}
	return a.enrichEmployees(ctx, toEmployees(employees)), nil
}

func (a *RepositoryAdapter) ListEmployees(ctx context.Context) ([]Employee, error) {
	if a == nil || a.employees == nil {
		return []Employee{}, nil
	}
	employees, err := a.employees.List(ctx)
	if err != nil {
		return nil, err
	}
	return a.enrichEmployees(ctx, toEmployees(employees)), nil
}

func (a *RepositoryAdapter) ListDepartments(ctx context.Context) ([]Department, error) {
	if a == nil || a.departments == nil {
		return []Department{}, nil
	}
	departments, err := a.departments.List(ctx)
	if err != nil {
		return nil, err
	}
	return toDepartments(departments), nil
}

func (a *RepositoryAdapter) GetEmployeesByDepartment(ctx context.Context, departmentID string) ([]Employee, error) {
	if a == nil || a.employees == nil {
		return []Employee{}, nil
	}
	employees, err := a.employees.ListByDepartment(ctx, departmentID)
	if err != nil {
		return nil, err
	}
	return a.enrichEmployees(ctx, toEmployees(employees)), nil
}

func (a *RepositoryAdapter) ListByLarkIDs(ctx context.Context, ids []string) ([]Department, error) {
	if a == nil || a.departments == nil {
		return []Department{}, nil
	}
	departments, err := a.departments.ListByLarkIDs(ctx, ids)
	if err != nil {
		return nil, err
	}
	return toDepartments(departments), nil
}

func toEmployee(employee depmodel.Employee) Employee {
	return Employee{
		ID:            employee.ID,
		Name:          employee.Name,
		Email:         employee.Email,
		LarkUserID:    employee.LarkUserID,
		DepartmentID:  employee.DepartmentID,
		DepartmentIDs: []string(employee.DepartmentIDs),
		Status:        employee.Status,
		AvatarURL:     employee.AvatarURL,
		JobTitle:      employee.JobTitle,
	}
}

func toEmployees(employees []depmodel.Employee) []Employee {
	items := make([]Employee, 0, len(employees))
	for _, employee := range employees {
		items = append(items, toEmployee(employee))
	}
	return items
}

func toDepartment(department depmodel.Department) Department {
	return Department{
		ID:               department.ID,
		Name:             department.Name,
		LarkDepartmentID: department.LarkDepartmentID,
		ParentID:         department.ParentID,
		LeaderID:         department.LeaderID,
		Status:           department.Status,
	}
}

func toDepartments(departments []depmodel.Department) []Department {
	items := make([]Department, 0, len(departments))
	for _, department := range departments {
		items = append(items, toDepartment(department))
	}
	return items
}

func (a *RepositoryAdapter) enrichEmployee(ctx context.Context, employee Employee) Employee {
	items := a.enrichEmployees(ctx, []Employee{employee})
	if len(items) == 0 {
		return employee
	}
	return items[0]
}

func (a *RepositoryAdapter) enrichEmployees(ctx context.Context, employees []Employee) []Employee {
	if a == nil || a.departments == nil || len(employees) == 0 {
		return employees
	}

	deptSet := make(map[string]struct{})
	for _, employee := range employees {
		if employee.DepartmentID != "" {
			deptSet[employee.DepartmentID] = struct{}{}
		}
		for _, departmentID := range employee.DepartmentIDs {
			if departmentID != "" {
				deptSet[departmentID] = struct{}{}
			}
		}
	}
	if len(deptSet) == 0 {
		return employees
	}

	deptIDs := make([]string, 0, len(deptSet))
	for departmentID := range deptSet {
		deptIDs = append(deptIDs, departmentID)
	}
	departments, err := a.departments.ListByLarkIDs(ctx, deptIDs)
	if err != nil {
		return employees
	}

	nameByID := make(map[string]string, len(departments))
	for _, department := range departments {
		nameByID[department.LarkDepartmentID] = department.Name
	}

	out := make([]Employee, 0, len(employees))
	for _, employee := range employees {
		if employee.DepartmentID != "" {
			employee.DepartmentName = nameByID[employee.DepartmentID]
		}
		out = append(out, employee)
	}

	return out
}
