package routes

import (
	"bssapp-backend/auth"
	"database/sql"
	"encoding/json"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"

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

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

type orderPriceListUserPriceGroupRow struct {
	UserID      int64    `json:"user_id"`
	Username    string   `json:"username"`
	FullName    string   `json:"full_name"`
	Email       string   `json:"email"`
	PriceGroups []string `json:"price_groups"`
}

type orderPriceListUserPriceGroupPayload struct {
	PriceGroups []string `json:"price_groups"`
}

var orderPriceListAllPriceGroups = []orderPriceListPriceGroupOption{
	{Value: "usd1", Label: "USD 1"},
	{Value: "usd2", Label: "USD 2"},
	{Value: "usd3", Label: "USD 3"},
	{Value: "usd4", Label: "USD 4"},
	{Value: "usd5", Label: "USD 5"},
	{Value: "usd6", Label: "USD 6"},
	{Value: "eur1", Label: "EUR 1"},
	{Value: "eur2", Label: "EUR 2"},
	{Value: "eur3", Label: "EUR 3"},
	{Value: "eur4", Label: "EUR 4"},
	{Value: "eur5", Label: "EUR 5"},
	{Value: "eur6", Label: "EUR 6"},
	{Value: "try1", Label: "TRY 1"},
	{Value: "try2", Label: "TRY 2"},
	{Value: "try3", Label: "TRY 3"},
	{Value: "try4", Label: "TRY 4"},
	{Value: "try5", Label: "TRY 5"},
	{Value: "try6", Label: "TRY 6"},
}

func ensureOrderPriceListUserPriceGroupSchema(db *sql.DB) error {
	stmts := []string{
		`CREATE TABLE IF NOT EXISTS mk_order_price_list_user_price_group (
			user_id BIGINT NOT NULL,
			price_group TEXT NOT NULL,
			created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
			updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
			PRIMARY KEY (user_id, price_group)
		)`,
		`CREATE INDEX IF NOT EXISTS ix_order_price_list_user_price_group_user ON mk_order_price_list_user_price_group (user_id)`,
		`ALTER TABLE mk_order_price_list_user_price_group DROP CONSTRAINT IF EXISTS ck_order_price_list_user_price_group`,
		`ALTER TABLE mk_order_price_list_user_price_group ADD CONSTRAINT ck_order_price_list_user_price_group CHECK (price_group IN ('usd1','usd2','usd3','usd4','usd5','usd6','eur1','eur2','eur3','eur4','eur5','eur6','try1','try2','try3','try4','try5','try6'))`,
	}
	for _, stmt := range stmts {
		if _, err := db.Exec(stmt); err != nil {
			return err
		}
	}
	return nil
}

func normalizeOrderPriceListPriceGroups(groups []string) []string {
	allowed := map[string]bool{}
	order := []string{}
	for _, opt := range orderPriceListAllPriceGroups {
		allowed[opt.Value] = true
		order = append(order, opt.Value)
	}
	set := map[string]bool{}
	for _, item := range groups {
		v := strings.ToLower(strings.TrimSpace(item))
		if allowed[v] {
			set[v] = true
		}
	}
	out := make([]string, 0, len(set))
	for _, v := range order {
		if set[v] {
			out = append(out, v)
		}
	}
	return out
}

func loadOrderPriceListUserPriceGroups(db *sql.DB, userID int64) ([]string, error) {
	if err := ensureOrderPriceListUserPriceGroupSchema(db); err != nil {
		return nil, err
	}
	rows, err := db.Query(`
		SELECT price_group
		FROM mk_order_price_list_user_price_group
		WHERE user_id = $1
		ORDER BY
			CASE SUBSTRING(price_group, 1, 3)
				WHEN 'usd' THEN 1
				WHEN 'eur' THEN 2
				WHEN 'try' THEN 3
				ELSE 9
			END,
			CAST(SUBSTRING(price_group, 4) AS INT)
	`, userID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var out []string
	for rows.Next() {
		var group string
		if err := rows.Scan(&group); err != nil {
			return nil, err
		}
		out = append(out, strings.ToLower(strings.TrimSpace(group)))
	}
	return normalizeOrderPriceListPriceGroups(out), rows.Err()
}

func saveOrderPriceListUserPriceGroupsTx(tx *sql.Tx, userID int64, groups []string) error {
	if _, err := tx.Exec(`DELETE FROM mk_order_price_list_user_price_group WHERE user_id = $1`, userID); err != nil {
		return err
	}
	for _, group := range normalizeOrderPriceListPriceGroups(groups) {
		if _, err := tx.Exec(`
			INSERT INTO mk_order_price_list_user_price_group (user_id, price_group, created_at, updated_at)
			VALUES ($1, $2, $3, $3)
			ON CONFLICT (user_id, price_group)
			DO UPDATE SET updated_at = EXCLUDED.updated_at
		`, userID, group, time.Now()); err != nil {
			return err
		}
	}
	return nil
}

func GetOrderPriceListPriceGroupLookupsHandler(db *sql.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		if err := ensureOrderPriceListUserPriceGroupSchema(db); err != nil {
			log.Printf("[order-price-list-price-groups] schema error: %v", err)
			http.Error(w, "price group schema error", http.StatusInternalServerError)
			return
		}
		_ = json.NewEncoder(w).Encode(map[string]any{"price_groups": orderPriceListAllPriceGroups})
	}
}

func GetMyOrderPriceListPriceGroupsHandler(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", http.StatusUnauthorized)
			return
		}
		groups, err := loadOrderPriceListUserPriceGroups(db, int64(claims.ID))
		if err != nil {
			log.Printf("[order-price-list-price-groups] my groups error user=%d err=%v", claims.ID, err)
			http.Error(w, "price groups lookup error", http.StatusInternalServerError)
			return
		}
		_ = json.NewEncoder(w).Encode(map[string]any{
			"price_groups": groups,
			"restricted":   len(groups) > 0,
			"all_groups":   orderPriceListAllPriceGroups,
		})
	}
}

func GetUserOrderPriceListPriceGroupsHandler(db *sql.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		id, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64)
		if err != nil || id <= 0 {
			http.Error(w, "invalid user id", http.StatusBadRequest)
			return
		}
		groups, err := loadOrderPriceListUserPriceGroups(db, id)
		if err != nil {
			log.Printf("[order-price-list-price-groups] user groups error user=%d err=%v", id, err)
			http.Error(w, "price groups lookup error", http.StatusInternalServerError)
			return
		}
		_ = json.NewEncoder(w).Encode(map[string]any{"price_groups": groups})
	}
}

func SaveUserOrderPriceListPriceGroupsHandler(db *sql.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		id, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64)
		if err != nil || id <= 0 {
			http.Error(w, "invalid user id", http.StatusBadRequest)
			return
		}
		var payload orderPriceListUserPriceGroupPayload
		if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
			http.Error(w, "invalid payload", http.StatusBadRequest)
			return
		}
		if err := ensureOrderPriceListUserPriceGroupSchema(db); err != nil {
			http.Error(w, "price group schema error", http.StatusInternalServerError)
			return
		}
		tx, err := db.Begin()
		if err != nil {
			http.Error(w, "transaction error", http.StatusInternalServerError)
			return
		}
		defer tx.Rollback()
		if err := saveOrderPriceListUserPriceGroupsTx(tx, id, payload.PriceGroups); err != nil {
			log.Printf("[order-price-list-price-groups] save error user=%d err=%v", id, err)
			http.Error(w, "price groups save error", http.StatusInternalServerError)
			return
		}
		if err := tx.Commit(); err != nil {
			http.Error(w, "commit error", http.StatusInternalServerError)
			return
		}
		_ = json.NewEncoder(w).Encode(map[string]any{"success": true})
	}
}

func GetOrderPriceListUserPriceGroupRowsHandler(db *sql.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		if err := ensureOrderPriceListUserPriceGroupSchema(db); err != nil {
			http.Error(w, "price group schema error", http.StatusInternalServerError)
			return
		}
		rows, err := db.Query(`
			SELECT u.id, u.username, COALESCE(u.full_name, ''), COALESCE(u.email, ''),
				COALESCE(array_agg(m.price_group ORDER BY
					CASE SUBSTRING(m.price_group, 1, 3)
						WHEN 'usd' THEN 1
						WHEN 'eur' THEN 2
						WHEN 'try' THEN 3
						ELSE 9
					END,
					CAST(SUBSTRING(m.price_group, 4) AS INT)
				) FILTER (WHERE m.price_group IS NOT NULL), ARRAY[]::text[]) AS price_groups
			FROM mk_dfusr u
			LEFT JOIN mk_order_price_list_user_price_group m ON m.user_id = u.id
			WHERE COALESCE(u.is_active, TRUE) = TRUE
			GROUP BY u.id, u.username, u.full_name, u.email
			ORDER BY u.username
		`)
		if err != nil {
			log.Printf("[order-price-list-price-groups] rows error: %v", err)
			http.Error(w, "price groups rows error", http.StatusInternalServerError)
			return
		}
		defer rows.Close()
		out := []orderPriceListUserPriceGroupRow{}
		for rows.Next() {
			var row orderPriceListUserPriceGroupRow
			if err := rows.Scan(&row.UserID, &row.Username, &row.FullName, &row.Email, pq.Array(&row.PriceGroups)); err != nil {
				http.Error(w, "price groups scan error", http.StatusInternalServerError)
				return
			}
			row.PriceGroups = normalizeOrderPriceListPriceGroups(row.PriceGroups)
			out = append(out, row)
		}
		_ = json.NewEncoder(w).Encode(out)
	}
}
