package routes

import (
	"bssapp-backend/auth"
	"bssapp-backend/internal/auditlog"
	"bssapp-backend/permissions"
	"bssapp-backend/queries"
	"database/sql"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"strings"

	"github.com/gorilla/mux"
)

type IdTitleOption struct {
	ID    string `json:"id"`
	Title string `json:"title"`
}
type Row struct {
	Route     string `json:"route"`
	CanAccess bool   `json:"can_access"`
}

type RoleDeptPermissionSummary struct {
	RoleID          int             `json:"role_id"`
	RoleTitle       string          `json:"role_title"`
	DepartmentCode  string          `json:"department_code"`
	DepartmentTitle string          `json:"department_title"`
	ModuleFlags     map[string]bool `json:"module_flags"`
}

type ModuleLookupOption struct {
	Value string `json:"value"`
	Label string `json:"label"`
}

type ModuleActionLookupOption struct {
	ModuleCode string `json:"module_code"`
	Action     string `json:"action"`
}

type RoleDeptPermissionListResponse struct {
	Modules       []ModuleLookupOption        `json:"modules"`
	ModuleActions []ModuleActionLookupOption  `json:"module_actions"`
	Rows          []RoleDeptPermissionSummary `json:"rows"`
}

type RoleDepartmentPermissionHandler struct {
	DB   *sql.DB
	Repo *permissions.RoleDepartmentPermissionRepo
}

func NewRoleDepartmentPermissionHandler(db *sql.DB) *RoleDepartmentPermissionHandler {

	return &RoleDepartmentPermissionHandler{
		DB:   db, // ✅ EKLENDİ
		Repo: permissions.NewRoleDepartmentPermissionRepo(db),
	}
}

/* ======================================================
   LIST
====================================================== */

func (h *RoleDepartmentPermissionHandler) List(w http.ResponseWriter, r *http.Request) {

	claims, ok := auth.GetClaimsFromContext(r.Context())
	if !ok || claims == nil {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

	search := strings.TrimSpace(r.URL.Query().Get("search"))

	modRows, err := h.DB.Query(queries.GetModuleLookup)
	if err != nil {
		http.Error(w, "module lookup error", http.StatusInternalServerError)
		return
	}
	defer modRows.Close()

	modules := make([]ModuleLookupOption, 0, 32)
	for modRows.Next() {
		var m ModuleLookupOption
		if err := modRows.Scan(&m.Value, &m.Label); err != nil {
			http.Error(w, "module lookup scan error", http.StatusInternalServerError)
			return
		}
		modules = append(modules, m)
	}

	if err := modRows.Err(); err != nil {
		http.Error(w, "module lookup rows error", http.StatusInternalServerError)
		return
	}

	actionRows, err := h.DB.Query(queries.GetModuleActionLookup)
	if err != nil {
		http.Error(w, "module action lookup error", http.StatusInternalServerError)
		return
	}
	defer actionRows.Close()

	moduleActions := make([]ModuleActionLookupOption, 0, 128)
	for actionRows.Next() {
		var a ModuleActionLookupOption
		if err := actionRows.Scan(&a.ModuleCode, &a.Action); err != nil {
			http.Error(w, "module action scan error", http.StatusInternalServerError)
			return
		}
		moduleActions = append(moduleActions, a)
	}

	if err := actionRows.Err(); err != nil {
		http.Error(w, "module action rows error", http.StatusInternalServerError)
		return
	}

	rows, err := h.DB.Query(queries.ListRoleDepartmentPermissionSets, search)
	if err != nil {
		http.Error(w, "db error", http.StatusInternalServerError)
		return
	}
	defer rows.Close()

	list := make([]RoleDeptPermissionSummary, 0, 128)
	for rows.Next() {
		var item RoleDeptPermissionSummary
		var rawFlags []byte
		if err := rows.Scan(
			&item.RoleID,
			&item.RoleTitle,
			&item.DepartmentCode,
			&item.DepartmentTitle,
			&rawFlags,
		); err != nil {
			http.Error(w, "scan error", http.StatusInternalServerError)
			return
		}

		item.ModuleFlags = map[string]bool{}
		if len(rawFlags) > 0 {
			if err := json.Unmarshal(rawFlags, &item.ModuleFlags); err != nil {
				http.Error(w, "module flags parse error", http.StatusInternalServerError)
				return
			}
		}
		list = append(list, item)
	}

	if err := rows.Err(); err != nil {
		http.Error(w, "rows error", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	_ = json.NewEncoder(w).Encode(RoleDeptPermissionListResponse{
		Modules:       modules,
		ModuleActions: moduleActions,
		Rows:          list,
	})
}

/* ======================================================
   GET
====================================================== */

func (h *RoleDepartmentPermissionHandler) Get(w http.ResponseWriter, r *http.Request) {

	claims, ok := auth.GetClaimsFromContext(r.Context())
	if !ok || claims == nil {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

	vars := mux.Vars(r)

	roleID, err := strconv.Atoi(vars["roleId"])
	if err != nil || roleID <= 0 {
		http.Error(w, "invalid roleId", http.StatusBadRequest)
		return
	}

	deptCode := vars["deptCode"]
	if deptCode == "" {
		http.Error(w, "invalid deptCode", http.StatusBadRequest)
		return
	}

	list, err := h.Repo.Get(roleID, deptCode)
	if err != nil {
		http.Error(w, "db error", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	_ = json.NewEncoder(w).Encode(list)
}

/* ======================================================
   POST
====================================================== */

func (h *RoleDepartmentPermissionHandler) Save(w http.ResponseWriter, r *http.Request) {

	claims, ok := auth.GetClaimsFromContext(r.Context())
	if !ok || claims == nil {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

	vars := mux.Vars(r)

	roleID, err := strconv.Atoi(vars["roleId"])
	if err != nil || roleID <= 0 {
		http.Error(w, "invalid roleId", http.StatusBadRequest)
		return
	}

	deptCode := vars["deptCode"]
	if deptCode == "" {
		http.Error(w, "invalid deptCode", http.StatusBadRequest)
		return
	}

	var list []permissions.RoleDepartmentPermission
	if err := json.NewDecoder(r.Body).Decode(&list); err != nil {
		http.Error(w, "bad payload", http.StatusBadRequest)
		return
	}

	// ================= OLD =================
	oldRows, err := h.Repo.Get(roleID, deptCode)
	if err != nil {
		log.Println("OLD PERM LOAD ERROR:", err)
	}

	oldMap := map[string]bool{}
	for _, p := range oldRows {
		key := p.Module + ":" + p.Action
		oldMap[key] = p.Allowed
	}

	// ================= DIFF =================
	var changes []map[string]any
	for _, p := range list {
		key := p.Module + ":" + p.Action
		oldVal := oldMap[key]
		if oldVal != p.Allowed {
			changes = append(changes, map[string]any{
				"module": p.Module,
				"action": p.Action,
				"before": oldVal,
				"after":  p.Allowed,
			})
		}
	}

	// ================= SAVE =================
	if err := h.Repo.Save(roleID, deptCode, list); err != nil {
		http.Error(w, "save error", http.StatusInternalServerError)
		return
	}

	// ================= AUDIT =================
	if len(changes) > 0 {
		auditlog.Enqueue(r.Context(), auditlog.ActivityLog{
			ActionType:     "role_department_permission_change",
			ActionCategory: "role_permission",
			ActionTarget: fmt.Sprintf(
				"/api/roles/%d/departments/%s/permissions",
				roleID,
				deptCode,
			),
			Description: "role+department permissions updated",
			Username:    claims.Username,
			RoleCode:    claims.RoleCode,
			DfUsrID:     int64(claims.ID),
			ChangeBefore: map[string]any{
				"permissions": oldRows,
			},
			ChangeAfter: map[string]any{
				"changes": changes,
			},
			IsSuccess: true,
		})
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	_ = json.NewEncoder(w).Encode(map[string]bool{"success": true})
}
func GetModuleLookupRoute(db *sql.DB) http.HandlerFunc {

	return func(w http.ResponseWriter, r *http.Request) {

		rows, err := db.Query(queries.GetModuleLookup)
		if err != nil {
			http.Error(w, "db error", 500)
			return
		}
		defer rows.Close()

		type Row struct {
			Value string `json:"value"`
			Label string `json:"label"`
		}

		var list []Row

		for rows.Next() {

			var r Row

			if err := rows.Scan(
				&r.Value,
				&r.Label,
			); err != nil {
				http.Error(w, "scan error", 500)
				return
			}

			list = append(list, r)
		}

		json.NewEncoder(w).Encode(list)
	}
}
func GetRolesForPermissionSelectRoute(db *sql.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")

		rows, err := db.Query(queries.GetRolesForPermissionSelect)
		if err != nil {
			http.Error(w, "roles select error", 500)
			return
		}
		defer rows.Close()

		list := make([]IdTitleOption, 0)
		for rows.Next() {
			var o IdTitleOption
			if err := rows.Scan(&o.ID, &o.Title); err == nil {
				list = append(list, o)
			}
		}

		_ = json.NewEncoder(w).Encode(list)
	}
}

func GetDepartmentsForPermissionSelectRoute(db *sql.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")

		rows, err := db.Query(queries.GetDepartmentsForPermissionSelect)
		if err != nil {
			http.Error(w, "departments select error", 500)
			return
		}
		defer rows.Close()

		list := make([]IdTitleOption, 0)
		for rows.Next() {
			var o IdTitleOption
			if err := rows.Scan(&o.ID, &o.Title); err == nil {
				list = append(list, o)
			}
		}

		_ = json.NewEncoder(w).Encode(list)
	}
}
func GetUsersForPermissionSelectRoute(db *sql.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		w.Header().Set("Content-Type", "application/json; charset=utf-8")

		rows, err := db.Query(queries.GetUserLookupForPermission)
		if err != nil {
			http.Error(w, "users lookup error", http.StatusInternalServerError)
			return
		}
		defer rows.Close()

		list := make([]IdTitleOption, 0, 128)

		for rows.Next() {

			var o IdTitleOption

			if err := rows.Scan(&o.ID, &o.Title); err == nil {
				list = append(list, o)
			}
		}

		_ = json.NewEncoder(w).Encode(list)
	}
}

func (h *PermissionHandler) GetUserOverrides(w http.ResponseWriter, r *http.Request) {

	claims, ok := auth.GetClaimsFromContext(r.Context())
	if !ok || claims == nil {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

	userID, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64)
	if err != nil || userID <= 0 {
		http.Error(w, "invalid id", http.StatusBadRequest)
		return
	}

	list, err := h.Repo.GetUserOverridesByUserID(userID)
	if err != nil {
		log.Println("❌ USER OVERRIDE LOAD ERROR:", err)
		http.Error(w, "db error", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	_ = json.NewEncoder(w).Encode(list)
}
func GetUserRoutePermissionsHandler(db *sql.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		w.Header().Set("Content-Type", "application/json; charset=utf-8")

		claims, ok := auth.GetClaimsFromContext(r.Context())
		if !ok || claims == nil {
			http.Error(w, "unauthorized", 401)
			return
		}

		repo := permissions.NewPermissionRepository(db)

		// JWT’den departmanlar
		depts := claims.DepartmentCodes

		rows, err := db.Query(`
			SELECT DISTINCT
				module_code,
				action,
				path
			FROM mk_sys_routes
		`)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}
		defer rows.Close()

		type Row struct {
			Route     string `json:"route"`
			CanAccess bool   `json:"can_access"`
		}

		list := make([]Row, 0, 64)

		for rows.Next() {

			var module, action, path string

			if err := rows.Scan(
				&module,
				&action,
				&path,
			); err != nil {
				continue
			}

			allowed, err := repo.ResolvePermissionChain(
				int64(claims.ID),
				int64(claims.RoleID),
				depts,
				module,
				action,
			)

			if err != nil {
				log.Println("PERM RESOLVE ERROR:", err)
				continue
			}

			list = append(list, Row{
				Route:     path,
				CanAccess: allowed,
			})
		}

		_ = json.NewEncoder(w).Encode(list)
	}
}

func GetMyEffectivePermissions(db *sql.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		w.Header().Set("Content-Type", "application/json; charset=utf-8")

		claims, ok := auth.GetClaimsFromContext(r.Context())
		if !ok || claims == nil {
			http.Error(w, "unauthorized", 401)
			return
		}

		repo := permissions.NewPermissionRepository(db)

		// ✅ JWT'DEN DEPARTMENTS
		depts := claims.DepartmentCodes

		log.Printf("🧪 EFFECTIVE PERM | user=%d role=%d depts=%v",
			claims.ID,
			claims.RoleID,
			depts,
		)

		// all system perms
		all, err := db.Query(`
			SELECT DISTINCT module_code, action
			FROM mk_sys_routes
		`)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}
		defer all.Close()

		type Row struct {
			Module  string `json:"module"`
			Action  string `json:"action"`
			Allowed bool   `json:"allowed"`
		}

		list := make([]Row, 0, 128)

		for all.Next() {

			var m, a string
			if err := all.Scan(&m, &a); err != nil {
				continue
			}

			allowed, err := repo.ResolvePermissionChain(
				int64(claims.ID),
				int64(claims.RoleID),
				depts,
				m,
				a,
			)

			if err != nil {
				continue
			}

			list = append(list, Row{
				Module:  m,
				Action:  a,
				Allowed: allowed,
			})
		}

		_ = json.NewEncoder(w).Encode(list)
	}
}
