package routes

import (
	"bssapp-backend/auth"
	"bssapp-backend/internal/auditlog"
	"bssapp-backend/internal/authz"
	"bssapp-backend/internal/mailer"
	"bssapp-backend/internal/security"
	"bssapp-backend/middlewares"
	"bssapp-backend/models"
	"bssapp-backend/queries"
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/gorilla/mux"
	"github.com/lib/pq"
)

// ======================================================
// 👤 USER DETAIL ROUTE (GET + PUT)
// URL: /api/users/{id}
// ======================================================
type LoginUser struct {
	ID                  int64  `json:"id"`
	Username            string `json:"username"`
	Email               string `json:"email"`
	RoleID              int    `json:"role_id"`
	RoleCode            string `json:"role_code"`
	ForcePasswordChange bool   `json:"force_password_change"`
}

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

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

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

		switch r.Method {
		case http.MethodGet:
			handleUserGet(db, w, userID)
		case http.MethodPut:
			handleUserUpdate(db, w, r, userID)
		case http.MethodDelete:
			handleUserDelete(db, w, r, userID)
		case http.MethodOptions:
			w.WriteHeader(http.StatusOK)
		default:
			http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		}
	})
}

// ======================================================
// 📥 GET USER
// ======================================================
// ======================================================
// 📥 GET USER (FINAL - mk_dfusr Uyumlu)
// ======================================================
func handleUserGet(db *sql.DB, w http.ResponseWriter, userID int64) {

	var u models.UserDetail

	// --------------------------------------------------
	// 🟢 HEADER
	// --------------------------------------------------
	err := db.QueryRow(
		queries.GetUserHeader,
		userID,
	).Scan(
		&u.ID,
		&u.Code, // username
		&u.IsActive,
		&u.FullName,
		&u.Email,
		&u.Mobile,
		&u.Address,
		&u.HasPassword,
	)

	if err != nil {

		// detaylı log (çok önemli)
		fmt.Println("❌ [UserDetail] HEADER SCAN ERROR:", err)

		if err == sql.ErrNoRows {
			http.Error(w, "Kullanıcı bulunamadı", http.StatusNotFound)
			return
		}

		http.Error(w, "User header error", http.StatusInternalServerError)
		return
	}

	// --------------------------------------------------
	// 🟢 ROLES
	// --------------------------------------------------
	roleRows, err := db.Query(queries.GetUserRoles, userID)
	if err != nil {
		fmt.Println("⚠️ [UserDetail] ROLE QUERY:", err)
	} else {
		defer roleRows.Close()

		for roleRows.Next() {
			var code string

			if err := roleRows.Scan(&code); err == nil {
				u.Roles = append(u.Roles, code)
			}
		}
	}

	// --------------------------------------------------
	// 🟢 DEPARTMENTS
	// --------------------------------------------------
	deptRows, err := db.Query(queries.GetUserDepartments, userID)
	if err != nil {
		fmt.Println("⚠️ [UserDetail] DEPT QUERY:", err)
	} else {
		defer deptRows.Close()

		for deptRows.Next() {

			var d models.DeptOption

			if err := deptRows.Scan(&d.Code, &d.Title); err == nil {
				u.Departments = append(u.Departments, d)
			}
		}
	}

	// --------------------------------------------------
	// 🟢 PIYASALAR
	// --------------------------------------------------
	piyRows, err := db.Query(queries.GetUserPiyasalar, userID)
	if err != nil {
		fmt.Println("⚠️ [UserDetail] PIYASA QUERY:", err)
	} else {
		defer piyRows.Close()

		for piyRows.Next() {

			var p models.DeptOption

			if err := piyRows.Scan(&p.Code, &p.Title); err == nil {
				u.Piyasalar = append(u.Piyasalar, p)
			}
		}
	}

	// --------------------------------------------------
	// 🟢 NEBIM USERS
	// --------------------------------------------------
	nebimRows, err := db.Query(queries.GetUserNebim, userID)
	if err != nil {
		fmt.Println("⚠️ [UserDetail] NEBIM QUERY:", err)
	} else {
		defer nebimRows.Close()

		for nebimRows.Next() {

			var n models.NebimOption

			if err := nebimRows.Scan(
				&n.Username,
				&n.UserGroupCode,
			); err == nil {

				u.NebimUsers = append(u.NebimUsers, n)
			}
		}
	}

	if groups, err := loadOrderPriceListUserPriceGroups(db, userID); err == nil {
		u.OrderPriceListPriceGroups = groups
	} else {
		log.Printf("WARN [UserDetail] order price list price groups lookup failed user_id=%d err=%v", userID, err)
	}

	// --------------------------------------------------
	// 🟢 RESPONSE
	// --------------------------------------------------
	w.WriteHeader(http.StatusOK)

	if err := json.NewEncoder(w).Encode(u); err != nil {
		fmt.Println("❌ [UserDetail] JSON ENCODE:", err)
	}
}

// ======================================================
// ✍️ UPDATE USER (PUT)
// ======================================================
func handleUserUpdate(db *sql.DB, w http.ResponseWriter, r *http.Request, userID int64) {

	raw, _ := io.ReadAll(r.Body)

	fmt.Println("🟥 RAW BODY:", string(raw))

	// body tekrar okunabilsin diye reset
	r.Body = io.NopCloser(bytes.NewBuffer(raw))

	var payload models.UserWrite

	if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
		fmt.Printf("🟦 DECODED PAYLOAD: %+v\n", payload)

		fmt.Println("❌ JSON DECODE ERROR:", err)

		http.Error(w, "Geçersiz payload", http.StatusBadRequest)
		return
	}

	payload.Code = strings.TrimSpace(payload.Code)
	payload.FullName = strings.TrimSpace(payload.FullName)
	payload.Email = strings.TrimSpace(payload.Email)
	payload.Mobile = strings.TrimSpace(payload.Mobile)
	payload.Address = strings.TrimSpace(payload.Address)

	if payload.Code == "" {
		http.Error(w, "Kullanıcı kodu zorunludur", http.StatusUnprocessableEntity)
		return
	}

	tx, err := db.Begin()
	if err != nil {
		http.Error(w, "Transaction başlatılamadı", http.StatusInternalServerError)
		return
	}
	defer tx.Rollback()

	_, err = tx.Exec(
		queries.UpdateUserHeader,
		userID,
		payload.Code,
		payload.IsActive,
		payload.FullName,
		payload.Email,
		payload.Mobile,
		payload.Address,
	)
	if err != nil {
		log.Printf("❌ [UserDetail] UpdateUserHeader failed user_id=%d err=%v payload=%+v", userID, err, payload)
		http.Error(w, "Header güncellenemedi", http.StatusInternalServerError)
		return
	}

	// Keep mk_mail in sync for downstream mail mapping screens.
	if err := ensureMkMail(tx, payload.Email); err != nil {
		log.Printf("ERROR [UserDetail] ensureMkMail failed user_id=%d email=%q err=%v", userID, payload.Email, err)
		http.Error(w, "Mail kaydi guncellenemedi", http.StatusInternalServerError)
		return
	}

	if _, err := tx.Exec(`DELETE FROM dfrole_usr WHERE dfusr_id = $1`, userID); err != nil {
		log.Printf("❌ [UserDetail] delete roles failed user_id=%d err=%v", userID, err)
		http.Error(w, "Roller temizlenemedi", http.StatusInternalServerError)
		return
	}
	for _, code := range payload.Roles {
		code = strings.TrimSpace(code)
		if code == "" {
			continue
		}
		if _, err := tx.Exec(queries.InsertUserRole, userID, code); err != nil {
			log.Printf("❌ [UserDetail] insert role failed user_id=%d role=%q err=%v", userID, code, err)
			http.Error(w, "Rol eklenemedi", http.StatusInternalServerError)
			return
		}
	}

	if _, err := tx.Exec(`DELETE FROM dfusr_dprt WHERE dfusr_id = $1`, userID); err != nil {
		log.Printf("❌ [UserDetail] delete departments failed user_id=%d err=%v", userID, err)
		http.Error(w, "Departmanlar temizlenemedi", http.StatusInternalServerError)
		return
	}
	for _, d := range payload.Departments {
		code := strings.TrimSpace(d.Code)
		if code == "" {
			continue
		}
		if _, err := tx.Exec(queries.InsertUserDepartment, userID, code); err != nil {
			log.Printf("❌ [UserDetail] insert department failed user_id=%d dept=%q err=%v", userID, code, err)
			http.Error(w, "Departman eklenemedi", http.StatusInternalServerError)
			return
		}
	}

	if _, err := tx.Exec(`DELETE FROM dfusr_piyasa WHERE dfusr_id = $1`, userID); err != nil {
		log.Printf("❌ [UserDetail] delete piyasalar failed user_id=%d err=%v", userID, err)
		http.Error(w, "Piyasalar temizlenemedi", http.StatusInternalServerError)
		return
	}
	for _, p := range payload.Piyasalar {
		code := strings.TrimSpace(p.Code)
		if code == "" {
			continue
		}
		if _, err := tx.Exec(queries.InsertUserPiyasa, userID, code); err != nil {
			log.Printf("❌ [UserDetail] insert piyasa failed user_id=%d piyasa=%q err=%v", userID, code, err)
			http.Error(w, "Piyasa eklenemedi", http.StatusInternalServerError)
			return
		}
	}

	if _, err := tx.Exec(`DELETE FROM dfusr_nebim_user WHERE dfusr_id = $1`, userID); err != nil {
		log.Printf("❌ [UserDetail] delete nebim users failed user_id=%d err=%v", userID, err)
		http.Error(w, "Nebim kullanıcıları temizlenemedi", http.StatusInternalServerError)
		return
	}
	for _, n := range payload.NebimUsers {
		username := strings.TrimSpace(n.Username)
		if username == "" {
			continue
		}
		if _, err := tx.Exec(queries.InsertUserNebim, userID, username); err != nil {
			log.Printf("❌ [UserDetail] insert nebim user failed user_id=%d username=%q err=%v", userID, username, err)
			http.Error(w, "Nebim kullanıcısı eklenemedi", http.StatusInternalServerError)
			return
		}
	}

	if err := ensureOrderPriceListUserPriceGroupSchema(db); err != nil {
		log.Printf("ERROR [UserDetail] price group schema failed user_id=%d err=%v", userID, err)
		http.Error(w, "Fiyat grubu tablosu hazirlanamadi", http.StatusInternalServerError)
		return
	}
	if err := saveOrderPriceListUserPriceGroupsTx(tx, userID, payload.OrderPriceListPriceGroups); err != nil {
		log.Printf("ERROR [UserDetail] price groups save failed user_id=%d err=%v", userID, err)
		http.Error(w, "Fiyat gruplari guncellenemedi", http.StatusInternalServerError)
		return
	}

	if err := tx.Commit(); err != nil {
		log.Printf("❌ [UserDetail] commit failed user_id=%d err=%v", userID, err)
		http.Error(w, "Commit başarısız", http.StatusInternalServerError)
		return
	}

	authz.ClearPiyasaCache(int(userID))
	middlewares.ClearAuthzScopeCacheForUser(userID)

	_ = json.NewEncoder(w).Encode(map[string]any{"success": true})
}

// ======================================================
// 🗑️ DELETE USER (HARD DELETE)
// ======================================================
func handleUserDelete(db *sql.DB, w http.ResponseWriter, r *http.Request, userID int64) {
	claims, _ := auth.GetClaimsFromContext(r.Context())
	if claims != nil && int64(claims.ID) == userID {
		http.Error(w, "Kendi kullanicinizi silemezsiniz", http.StatusConflict)
		return
	}

	tx, err := db.Begin()
	if err != nil {
		http.Error(w, "Transaction baslatilamadi", http.StatusInternalServerError)
		return
	}
	defer tx.Rollback()

	var username string
	_ = tx.QueryRow(`
		SELECT username
		FROM mk_dfusr
		WHERE id = $1
	`, userID).Scan(&username)

	if strings.TrimSpace(username) == "" {
		_ = tx.QueryRow(`
			SELECT code
			FROM dfusr
			WHERE id = $1
		`, userID).Scan(&username)
	}

	if strings.TrimSpace(username) == "" {
		http.Error(w, "Kullanici bulunamadi", http.StatusNotFound)
		return
	}

	cleanupQueries := []string{
		`DELETE FROM mk_refresh_tokens WHERE mk_user_id = $1`,
		`DELETE FROM mk_dfusr_password_reset WHERE mk_dfusr_id = $1`,
		`DELETE FROM dfusr_password_reset WHERE dfusr_id = $1`,
		`DELETE FROM mk_sys_user_permissions WHERE user_id = $1`,
		`DELETE FROM dfrole_usr WHERE dfusr_id = $1`,
		`DELETE FROM dfusr_dprt WHERE dfusr_id = $1`,
		`DELETE FROM dfusr_piyasa WHERE dfusr_id = $1`,
		`DELETE FROM dfusr_nebim_user WHERE dfusr_id = $1`,
		`DELETE FROM mk_order_price_list_user_price_group WHERE user_id = $1`,
	}

	isUndefinedTable := func(err error) bool {
		if err == nil {
			return false
		}
		if pe, ok := err.(*pq.Error); ok {
			return pe.Code == "42P01" // undefined_table
		}
		return false
	}

	for i, q := range cleanupQueries {
		sp := fmt.Sprintf("sp_user_cleanup_%d", i)
		_, _ = tx.Exec("SAVEPOINT " + sp)

		if _, err := tx.Exec(q, userID); err != nil {
			// rollback the failed statement so tx can continue
			_, _ = tx.Exec("ROLLBACK TO SAVEPOINT " + sp)

			if isUndefinedTable(err) {
				log.Printf("⚠️ [UserDetail] cleanup skipped (table missing) user_id=%d query=%s", userID, q)
				_, _ = tx.Exec("RELEASE SAVEPOINT " + sp)
				continue
			}
			if pe, ok := err.(*pq.Error); ok {
				log.Printf(
					"❌ [UserDetail] cleanup failed user_id=%d code=%s detail=%s constraint=%s table=%s err=%v query=%s",
					userID, pe.Code, pe.Detail, pe.Constraint, pe.Table, err, q,
				)
			} else {
				log.Printf("❌ [UserDetail] cleanup failed user_id=%d err=%v query=%s", userID, err, q)
			}
			http.Error(w, "Kullanici baglantilari silinemedi", http.StatusInternalServerError)
			return
		}

		_, _ = tx.Exec("RELEASE SAVEPOINT " + sp)
	}

	if _, err := tx.Exec(`DELETE FROM mk_dfusr WHERE id = $1`, userID); err != nil {
		log.Printf("❌ [UserDetail] delete mk_dfusr failed user_id=%d err=%v", userID, err)
		http.Error(w, "Kullanici silinemedi", http.StatusInternalServerError)
		return
	}

	if err := tx.Commit(); err != nil {
		log.Printf("❌ [UserDetail] delete commit failed user_id=%d err=%v", userID, err)
		http.Error(w, "Commit basarisiz", http.StatusInternalServerError)
		return
	}

	authz.ClearPiyasaCache(int(userID))
	middlewares.ClearAuthzScopeCacheForUser(userID)

	if claims != nil {
		auditlog.Enqueue(r.Context(), auditlog.ActivityLog{
			ActionType:     "user_delete",
			ActionCategory: "user_admin",
			ActionTarget:   fmt.Sprintf("/api/users/%d", userID),
			Description:    "user deleted from mk_dfusr (dfusr retained)",
			Username:       claims.Username,
			RoleCode:       claims.RoleCode,
			DfUsrID:        int64(claims.ID),
			TargetDfUsrID:  userID,
			TargetUsername: username,
			IsSuccess:      true,
		})
	}

	_ = json.NewEncoder(w).Encode(map[string]any{
		"success":  true,
		"deleted":  userID,
		"username": username,
	})
}

// ======================================================
// 🔐 ADMIN — PASSWORD RESET MAIL
// ======================================================
func SendPasswordResetMailHandler(
	db *sql.DB,
	mailer *mailer.GraphMailer,
) http.HandlerFunc {

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

		userID, _ := strconv.ParseInt(mux.Vars(r)["id"], 10, 64)

		var email string
		err := db.QueryRow(`
				SELECT email
				FROM mk_dfusr
				WHERE id = $1 AND is_active = true
			`, userID).Scan(&email)

		if err != nil || email == "" {
			w.WriteHeader(http.StatusOK)
			return
		}

		// 🔑 TOKEN (PLAIN + HASH)
		plain, hash, err := security.GenerateResetToken()
		if err != nil {
			w.WriteHeader(http.StatusOK)
			return
		}

		expires := time.Now().Add(30 * time.Minute)

		// 💾 DB → SADECE HASH
		_, _ = db.Exec(`
			INSERT INTO dfusr_password_reset (dfusr_id, token_hash, expires_at)
			VALUES ($1,$2,$3)
		`, userID, hash, expires)

		// 🔗 URL → PLAIN
		resetURL := security.BuildResetURL(plain)

		_ = mailer.SendPasswordResetMail(email, resetURL)

		// 🕵️ AUDIT
		auditlog.ForcePasswordChangeStarted(
			r.Context(),
			userID,
			"admin_reset",
		)

		w.WriteHeader(http.StatusOK)
	}
}
