package permissions

import (
	"database/sql"
	"log"
	"strings"

	"github.com/lib/pq"
)

type PermissionRepository struct {
	DB *sql.DB
}

func NewPermissionRepository(db *sql.DB) *PermissionRepository {
	return &PermissionRepository{DB: db}
}

/* =====================================================
   MATRIX READ (V2) - ROLE BASED
===================================================== */

func (r *PermissionRepository) GetPermissionMatrixForRoles(
	roleIDs []int,
) ([]PermissionMatrixRow, error) {

	if len(roleIDs) == 0 {
		return []PermissionMatrixRow{}, nil
	}

	query := `
		SELECT
			rp.role_id,
			rol.code,
			rp.module_code,
			rp.action,
			rp.allowed
		FROM mk_sys_role_permissions rp
		JOIN dfrole rol ON rol.id = rp.role_id
		WHERE rp.role_id = ANY($1)
		ORDER BY rol.id, rp.module_code, rp.action
	`

	rows, err := r.DB.Query(query, pq.Array(roleIDs))
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	list := make([]PermissionMatrixRow, 0)

	for rows.Next() {
		var row PermissionMatrixRow
		if err := rows.Scan(
			&row.RoleID,
			&row.RoleCode,
			&row.Module,
			&row.Action,
			&row.Allowed,
		); err != nil {
			return nil, err
		}

		row.Source = "role"
		list = append(list, row)
	}

	return list, nil
}

/* =====================================================
   MATRIX UPDATE (V2) - ROLE BASED
===================================================== */

func (r *PermissionRepository) UpdatePermissions(
	list []PermissionUpdateRequest,
) error {

	tx, err := r.DB.Begin()
	if err != nil {
		return err
	}
	defer tx.Rollback()

	stmt, err := tx.Prepare(`
		INSERT INTO mk_sys_role_permissions
			(role_id, module_code, action, allowed)
		VALUES ($1,$2,$3,$4)
		ON CONFLICT (role_id, module_code, action)
		DO UPDATE SET allowed = EXCLUDED.allowed
	`)
	if err != nil {
		return err
	}
	defer stmt.Close()

	for _, p := range list {
		if _, err := stmt.Exec(
			p.RoleID,
			p.Module,
			p.Action,
			p.Allowed,
		); err != nil {
			return err
		}
	}

	return tx.Commit()
}

/* =====================================================
   USER OVERRIDES - READ
   GET /api/users/{id}/permissions
===================================================== */

// Tek tip: PermissionMatrixRow döndürüyoruz (source=user)
func (r *PermissionRepository) GetUserOverridesByUserID(
	userID int64,
) ([]PermissionMatrixRow, error) {

	rows, err := r.DB.Query(`
		SELECT
			user_id,
			module_code,
			action,
			allowed
		FROM mk_sys_user_permissions
		WHERE user_id = $1
		ORDER BY module_code, action
	`, userID)

	if err != nil {
		return nil, err
	}
	defer rows.Close()

	list := make([]PermissionMatrixRow, 0)

	for rows.Next() {
		var row PermissionMatrixRow
		if err := rows.Scan(
			&row.UserID,
			&row.Module,
			&row.Action,
			&row.Allowed,
		); err != nil {
			return nil, err
		}

		row.Source = "user"
		list = append(list, row)
	}

	return list, nil
}

/* =====================================================
   USER OVERRIDES - UPSERT SAVE (typed)
   POST /api/users/{id}/permissions
===================================================== */

func (r *PermissionRepository) SaveUserOverrides(
	userID int64,
	list []UserPermissionRequest,
) error {

	log.Println("➡️ REPO SaveUserOverrides START")
	log.Println("USER:", userID)
	log.Println("ROWS:", len(list))

	tx, err := r.DB.Begin()
	if err != nil {
		log.Println("❌ TX BEGIN ERROR:", err)
		return err
	}
	defer tx.Rollback()

	// önce sil
	_, err = tx.Exec(`
		DELETE FROM mk_sys_user_permissions
		WHERE user_id = $1
	`, userID)

	if err != nil {
		log.Println("❌ DELETE ERROR:", err)
		return err
	}

	stmt, err := tx.Prepare(`
		INSERT INTO mk_sys_user_permissions
			(user_id, module_code, action, allowed)
		VALUES ($1,$2,$3,$4)
	`)

	if err != nil {
		log.Println("❌ PREPARE ERROR:", err)
		return err
	}
	defer stmt.Close()

	for _, p := range list {
		if strings.TrimSpace(p.Module) == "" {
			log.Printf("⚠️ SKIP EMPTY MODULE user=%d action=%s",
				userID,
				p.Action,
			)
			continue
		}

		_, err := stmt.Exec(
			userID,
			p.Module,
			p.Action,
			p.Allowed,
		)
		if err != nil {
			log.Println("❌ INSERT ERROR:", err)
			return err
		}
	}

	if err := tx.Commit(); err != nil {
		log.Println("❌ COMMIT ERROR:", err)
		return err
	}

	log.Println("✅ REPO SaveUserOverrides DONE")
	return nil

}

/* =====================================================
   RESOLUTION HELPERS (middleware için)
===================================================== */

// user override var mı? varsa *bool döner, yoksa nil
func (r *PermissionRepository) HasUserOverride(
	userID int64,
	module string,
	action string,
) (*bool, error) {

	var allowed bool

	err := r.DB.QueryRow(`
		SELECT allowed
		FROM mk_sys_user_permissions
		WHERE user_id=$1
		  AND module_code=$2
		  AND action=$3
	`,
		userID,
		module,
		action,
	).Scan(&allowed)

	if err == sql.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}

	return &allowed, nil
}

// roleIDs içinden OR logic: herhangi biri allowed=true ise true
func (r *PermissionRepository) HasRoleAccess(
	roleIDs []int,
	module string,
	action string,
) (bool, error) {

	if len(roleIDs) == 0 {
		return false, nil
	}

	var count int

	err := r.DB.QueryRow(`
		SELECT COUNT(*)
		FROM mk_sys_role_permissions
		WHERE role_id = ANY($1)
		  AND module_code=$2
		  AND action=$3
		  AND allowed=true
	`,
		pq.Array(roleIDs),
		module,
		action,
	).Scan(&count)

	if err != nil {
		return false, err
	}

	return count > 0, nil
}

// Final decision: user override varsa onu uygula, yoksa role bazlı
func (r *PermissionRepository) ResolvePermission(
	userID int64,
	roleIDs []int,
	module string,
	action string,
) (bool, error) {

	override, err := r.HasUserOverride(userID, module, action)
	if err != nil {
		return false, err
	}

	if override != nil {
		return *override, nil
	}

	return r.HasRoleAccess(roleIDs, module, action)
}
func (r *PermissionRepository) GetUserOverrides(
	userID int64,
) ([]UserPermissionOverride, error) {

	rows, err := r.DB.Query(`
		SELECT module_code, action, allowed
		FROM mk_sys_user_permissions
		WHERE user_id = $1
	`, userID)

	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var list []UserPermissionOverride

	for rows.Next() {

		var o UserPermissionOverride

		if err := rows.Scan(
			&o.Module,
			&o.Action,
			&o.Allowed,
		); err != nil {
			return nil, err
		}

		list = append(list, o)
	}

	return list, nil
}
func (r *PermissionRepository) UpdateUserOverrides(
	list []UserPermission,
) error {

	tx, err := r.DB.Begin()
	if err != nil {
		return err
	}
	defer tx.Rollback()

	stmt, err := tx.Prepare(`
		INSERT INTO mk_sys_user_permissions
			(user_id, module_code, action, allowed)

		VALUES ($1,$2,$3,$4)

		ON CONFLICT (user_id, module_code, action)
		DO UPDATE SET allowed = EXCLUDED.allowed
	`)
	if err != nil {
		return err
	}
	defer stmt.Close()

	for _, p := range list {

		_, err := stmt.Exec(
			p.UserID,
			p.Module,
			p.Action,
			p.Allowed,
		)

		if err != nil {
			return err
		}
	}

	return tx.Commit()
}
func (r *PermissionRepository) ResolveEffectivePermission(
	userID int64,
	roleID int64,
	deptCode string,
	module string,
	action string,
) (bool, error) {

	// 1️⃣ USER OVERRIDE
	var allowed bool

	err := r.DB.QueryRow(`
		SELECT allowed
		FROM mk_sys_user_permissions
		WHERE user_id = $1
		  AND module_code = $2
		  AND action = $3
	`,
		userID, module, action,
	).Scan(&allowed)

	if err == nil {
		return allowed, nil
	}
	if err != sql.ErrNoRows {
		return false, err
	}

	// ==================================================
	// 2️⃣ ROLE + DEPARTMENT
	// ==================================================

	if len(deptCode) > 0 {

		var allowed bool

		err = r.DB.QueryRow(`   -- 🔥 := DEĞİL =
		SELECT allowed
		FROM vw_role_dept_permissions
		WHERE role_id = $1
		  AND department_code = ANY($2)
		  AND module_code = $3
		  AND action = $4
		ORDER BY allowed DESC
		LIMIT 1
	`,
			roleID,
			pq.Array([]string{deptCode}),
			module,
			action,
		).Scan(&allowed)

		if err == nil {

			log.Printf(
				"  ↳ ROLE+DEPT OVERRIDE = %v",
				allowed,
			)

			return allowed, nil
		}

		if err != sql.ErrNoRows {
			log.Println("❌ ROLE+DEPT ERR:", err)
			return false, err
		}
	}

	// 3️⃣ ROLE DEFAULT
	err = r.DB.QueryRow(`
		SELECT allowed
		FROM mk_sys_role_permissions
		WHERE role_id = $1
		  AND module_code = $2
		  AND action = $3
	`,
		roleID, module, action,
	).Scan(&allowed)

	if err == nil {
		return allowed, nil
	}
	if err != sql.ErrNoRows {
		return false, err
	}

	// 4️⃣ DENY
	return false, nil
}
func (r *PermissionRepository) ResolvePermissionChain(
	userID int64,
	roleID int64,
	deptCodes []string,
	module string,
	action string,
) (bool, error) {

	log.Printf(
		"🔐 PERM CHECK user=%d role=%d dept=%v %s:%s",
		userID,
		roleID,
		deptCodes,
		module,
		action,
	)

	// ==================================================
	// 1️⃣ USER OVERRIDE
	// ==================================================

	override, err := r.HasUserOverride(userID, module, action)
	if err != nil {
		log.Println("❌ USER OVERRIDE ERR:", err)
		return false, err
	}

	if override != nil {

		log.Printf(
			"  ↳ USER OVERRIDE = %v",
			*override,
		)

		return *override, nil
	}

	// ==================================================
	// 2️⃣ ROLE + DEPARTMENT
	// ==================================================

	if len(deptCodes) > 0 {

		var allowed bool

		err := r.DB.QueryRow(`
    SELECT allowed
    FROM vw_role_dept_permissions
    WHERE role_id = $1
      AND department_code IN (
          SELECT UNNEST($2::text[])
      )
      AND module_code = $3
      AND action = $4
    ORDER BY allowed DESC
    LIMIT 1
`,
			roleID,
			pq.Array(deptCodes),
			module,
			action,
		).Scan(&allowed)

		if err == nil {

			log.Printf(
				"  ↳ ROLE+DEPT OVERRIDE = %v",
				allowed,
			)

			return allowed, nil
		}

		if err != sql.ErrNoRows {
			log.Println("❌ ROLE+DEPT ERR:", err)
			return false, err
		}
	}

	// ==================================================
	// 3️⃣ ROLE DEFAULT
	// ==================================================

	var roleAllowed bool

	err = r.DB.QueryRow(`
		SELECT allowed
		FROM mk_sys_role_permissions
		WHERE role_id = $1
		  AND module_code = $2
		  AND action = $3
	`,
		roleID,
		module,
		action,
	).Scan(&roleAllowed)

	if err == nil {

		log.Printf(
			"  ↳ ROLE DEFAULT = %v",
			roleAllowed,
		)

		return roleAllowed, nil
	}

	if err != sql.ErrNoRows {
		log.Println("❌ ROLE DEFAULT ERR:", err)
		return false, err
	}

	// ==================================================
	// 4️⃣ DENY
	// ==================================================

	log.Println("  ↳ NO RULE → DENY")

	return false, nil
}
