package handler

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"Engine/internal/database"
	"Engine/internal/model"
)

type ReferenceHandler struct {
	repo *database.ReferenceRepo
}

func NewReferenceHandler(repo *database.ReferenceRepo) *ReferenceHandler {
	return &ReferenceHandler{repo: repo}
}

// ── Departments ───────────────────────────────────────────────────────────────

// POST /api/v1/department/create
// PUT  /api/v1/departments/{id}  (same body, id from path)
func (h *ReferenceHandler) CreateDepartment(w http.ResponseWriter, r *http.Request) {
	var req model.CreateDepartmentRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, fmt.Sprintf("invalid body: %v", err))
		return
	}
	req.Code = strings.TrimSpace(req.Code)
	req.Name = strings.TrimSpace(req.Name)
	if req.Code == "" || req.Name == "" {
		writeError(w, http.StatusBadRequest, "code and name are required")
		return
	}
	// Default active to true when not explicitly set to false
	if !req.Active {
		req.Active = true
	}

	dept, err := h.repo.CreateDepartment(r.Context(), req)
	if err != nil {
		writeError(w, http.StatusInternalServerError, fmt.Sprintf("create department: %v", err))
		return
	}
	writeJSON(w, http.StatusCreated, dept)
}

// GET /api/v1/department/list
func (h *ReferenceHandler) ListDepartments(w http.ResponseWriter, r *http.Request) {
	depts, err := h.repo.ListDepartments(r.Context())
	if err != nil {
		writeError(w, http.StatusInternalServerError, fmt.Sprintf("list departments: %v", err))
		return
	}
	if depts == nil {
		depts = []model.Department{}
	}
	writeJSON(w, http.StatusOK, depts)
}

// GET /api/v1/department/
func (h *ReferenceHandler) GetDepartment(w http.ResponseWriter, r *http.Request) {
	var req model.GetDepartmentRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, fmt.Sprintf("invalid body: %v", err))
		return
	}

	// exactly one field must be set
	set := 0
	if req.Code != nil {
		set++
	}
	if req.Name != nil {
		set++
	}
	if req.CostCenter != nil {
		set++
	}
	if set == 0 {
		writeError(w, http.StatusBadRequest, "one of code, name, cost_center is required")
		return
	}
	if set > 1 {
		writeError(w, http.StatusBadRequest, "only one of code, name, cost_center may be set")
		return
	}

	var (
		dept *model.Department
		err  error
	)
	switch {
	case req.Code != nil:
		dept, err = h.repo.GetDepartmentByCode(r.Context(), *req.Code)
	case req.Name != nil:
		dept, err = h.repo.GetDepartmentByName(r.Context(), *req.Name)
	case req.CostCenter != nil:
		dept, err = h.repo.GetDepartmentByCostCenter(r.Context(), *req.CostCenter)
	}
	if err != nil {
		writeError(w, http.StatusInternalServerError, err.Error())
		return
	}
	if dept == nil {
		writeError(w, http.StatusNotFound, "department not found")
		return
	}

	writeJSON(w, http.StatusOK, dept)
}

func (h *ReferenceHandler) SetActivityDepartment(w http.ResponseWriter, r *http.Request) {
	var req model.SetDepartmentActivity
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, fmt.Sprintf("invalid body: %v", err))
		return
	}

	set := 0
	if req.Code != nil {
		set++
	}
	if req.Name != nil {
		set++
	}
	if req.CostCenter != nil {
		set++
	}
	if set == 0 {
		writeError(w, http.StatusBadRequest, "one of code, name, cost_center is required")
		return
	}
	if set > 1 {
		writeError(w, http.StatusBadRequest, "only one of code, name, cost_center may be set")
		return
	}

	var err error
	switch {
	case req.Code != nil:
		err = h.repo.SetDepartmentActivityByCode(r.Context(), *req.Code, req.Active)
	case req.Name != nil:
		err = h.repo.SetDepartmentActivityByName(r.Context(), *req.Name, req.Active)
	case req.CostCenter != nil:
		err = h.repo.SetDepartmentActivityByCostCenter(r.Context(), *req.CostCenter, req.Active)
	}
	if err != nil {
		writeError(w, http.StatusInternalServerError, err.Error())
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

// DELETE /api/v1/department/delete
func (h *ReferenceHandler) DeleteDepartment(w http.ResponseWriter, r *http.Request) {
	var req model.DeleteDepartmentRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, fmt.Sprintf("invalid body: %v", err))
		return
	}
	if req.ID == 0 {
		writeError(w, http.StatusBadRequest, "id is required")
		return
	}
	if err := h.repo.DeleteDepartment(r.Context(), req); err != nil {
		writeError(w, http.StatusInternalServerError, fmt.Sprintf("delete department: %v", err))
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

// ── GL Accounts ───────────────────────────────────────────────────────────────

var validGLTypes = map[string]bool{
	"revenue": true, "cogs": true, "opex": true, "capex": true, "headcount": true,
}

// POST /api/v1/gl-accounts
func (h *ReferenceHandler) CreateGLAccount(w http.ResponseWriter, r *http.Request) {
	var req model.GLAccount
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, fmt.Sprintf("invalid body: %v", err))
		return
	}
	req.Code = strings.TrimSpace(req.Code)
	req.Description = strings.TrimSpace(req.Description)
	if req.Code == "" || req.Description == "" {
		writeError(w, http.StatusBadRequest, "code and description are required")
		return
	}
	if !validGLTypes[req.Type] {
		writeError(w, http.StatusBadRequest,
			"type must be one of: revenue, cogs, opex, capex, headcount")
		return
	}
	if !req.Active {
		req.Active = true
	}

	acct, err := h.repo.CreateGLAccount(r.Context(), req)
	if err != nil {
		writeError(w, http.StatusInternalServerError, fmt.Sprintf("create gl_account: %v", err))
		return
	}
	writeJSON(w, http.StatusCreated, acct)
}

// GET /api/v1/gl-accounts
func (h *ReferenceHandler) ListGLAccounts(w http.ResponseWriter, r *http.Request) {
	accts, err := h.repo.ListGLAccounts(r.Context())
	if err != nil {
		writeError(w, http.StatusInternalServerError, fmt.Sprintf("list gl_accounts: %v", err))
		return
	}
	if accts == nil {
		accts = []model.GLAccount{}
	}
	writeJSON(w, http.StatusOK, accts)
}

// DELETE /api/v1/gl-accounts/delete
func (h *ReferenceHandler) DeleteGLAccount(w http.ResponseWriter, r *http.Request) {
	var req model.DeleteGLAccountRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, fmt.Sprintf("invalid body: %v", err))
		return
	}
	if req.ID == 0 {
		writeError(w, http.StatusBadRequest, "id is required")
		return
	}
	if err := h.repo.DeleteGLAccount(r.Context(), req.ID); err != nil {
		writeError(w, http.StatusInternalServerError, fmt.Sprintf("delete gl_account: %v", err))
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

// ── Shared helpers ────────────────────────────────────────────────────────────

func pathID(r *http.Request, key string) (int, error) {
	return strconv.Atoi(r.PathValue(key))
}
