package handler

import (
	"errors"
	"net/http"
	"strconv"
	"strings"
	"time"

	"gorm.io/gorm"

	"security-center-backend/internal/model"
	depmodel "security-center-backend/internal/modules/department/model"
	depservice "security-center-backend/internal/modules/department/service"
	"security-center-backend/internal/pkg/response"
	"security-center-backend/internal/repository"

	"github.com/gin-gonic/gin"
)

const pickerReadsBlockedMessage = "org sync snapshot is expired; Feishu-backed picker reads are blocked until a fresh sync succeeds"

type Handler struct {
	deptSvc  *depservice.DepartmentService
	runtime  *depservice.OrgSyncRuntimeControl
	userRepo repository.UserRepository
}

func New(deptSvc *depservice.DepartmentService, runtime *depservice.OrgSyncRuntimeControl, userRepo repository.UserRepository) *Handler {
	return &Handler{deptSvc: deptSvc, runtime: runtime, userRepo: userRepo}
}

func (h *Handler) RegisterRoutes(rg *gin.RouterGroup) {
	rg.GET("", h.ListEmployees)
	rg.GET("/by-email/:email", h.GetEmployeeByEmail)
	rg.GET("/:id", h.GetEmployee)
}

type employeeLinkStatus struct {
	depmodel.Employee
	LinkedUserID       *uint64                  `json:"linked_user_id,omitempty"`
	FirstLoginAt       *time.Time               `json:"first_login_at,omitempty"`
	EmployeeLinkStatus model.EmployeeLinkStatus `json:"employee_link_status,omitempty"`
}

func (h *Handler) ListEmployees(c *gin.Context) {
	if h.pickerReadsBlocked() {
		response.Error(c, http.StatusForbidden, pickerReadsBlockedMessage)
		return
	}
	employees, err := h.deptSvc.ListEmployees(c.Request.Context())
	if err != nil {
		response.Error(c, http.StatusInternalServerError, err.Error())
		return
	}

	if h.userRepo == nil {
		response.Success(c, employees)
		return
	}

	emailKeys := make([]string, 0, len(employees))
	for _, emp := range employees {
		if emp.Email == "" {
			continue
		}
		emailKeys = append(emailKeys, strings.ToLower(strings.TrimSpace(emp.Email)))
	}
	usersByEmail := make(map[string]model.User)
	if len(emailKeys) > 0 {
		users, err := h.userRepo.FindByEmails(c.Request.Context(), emailKeys)
		if err != nil {
			response.Error(c, http.StatusInternalServerError, err.Error())
			return
		}
		for _, user := range users {
			if user.Email == "" {
				continue
			}
			usersByEmail[strings.ToLower(strings.TrimSpace(user.Email))] = user
		}
	}

	payload := make([]employeeLinkStatus, 0, len(employees))
	for _, emp := range employees {
		entry := employeeLinkStatus{
			Employee:           emp,
			EmployeeLinkStatus: model.EmployeeLinkStatusPending,
		}
		if emp.Email != "" {
			if user, ok := usersByEmail[strings.ToLower(strings.TrimSpace(emp.Email))]; ok {
				entry.LinkedUserID = &user.ID
				entry.FirstLoginAt = user.FirstLoginAt
				if user.EmployeeLinkStatus != "" {
					entry.EmployeeLinkStatus = user.EmployeeLinkStatus
				}
			}
		}
		payload = append(payload, entry)
	}
	response.Success(c, payload)
}

func (h *Handler) GetEmployee(c *gin.Context) {
	if h.pickerReadsBlocked() {
		response.Error(c, http.StatusForbidden, pickerReadsBlockedMessage)
		return
	}
	id, err := strconv.ParseUint(c.Param("id"), 10, 64)
	if err != nil {
		response.Error(c, http.StatusBadRequest, "invalid id")
		return
	}
	emp, err := h.deptSvc.GetEmployeeByID(c.Request.Context(), id)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			response.Error(c, http.StatusNotFound, "employee not found")
			return
		}
		response.Error(c, http.StatusInternalServerError, err.Error())
		return
	}
	response.Success(c, emp)
}

func (h *Handler) GetEmployeeByEmail(c *gin.Context) {
	if h.pickerReadsBlocked() {
		response.Error(c, http.StatusForbidden, pickerReadsBlockedMessage)
		return
	}
	email := strings.TrimSpace(c.Param("email"))
	if email == "" {
		response.Error(c, http.StatusBadRequest, "email is required")
		return
	}
	emp, err := h.deptSvc.GetEmployeeByEmail(c.Request.Context(), email)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			response.Success(c, nil)
			return
		}
		response.Error(c, http.StatusInternalServerError, err.Error())
		return
	}
	response.Success(c, emp)
}

func (h *Handler) pickerReadsBlocked() bool {
	if h.runtime == nil {
		return false
	}
	status := h.runtime.Status()
	for _, scope := range status.BlockingScopes {
		if scope == "picker_reads" {
			return true
		}
	}
	return false
}
