package routes

import (
	"bssapp-backend/auth"
	"bssapp-backend/models"
	"bssapp-backend/queries"
	"database/sql"
	"encoding/json"
	"errors"
	"log"
	"net/http"
	"regexp"
	"strings"

	"github.com/gorilla/mux"
	mssql "github.com/microsoft/go-mssqldb"
)

var baggiModelCodeRegex = regexp.MustCompile(`^[A-Z][0-9]{3}-[A-Z]{3}[0-9]{5}$`)

// ======================================================
// 📌 OrderProductionItemsRoute — U ürün satırları
// ======================================================
func OrderProductionItemsRoute(mssql *sql.DB) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

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

		id := mux.Vars(r)["id"]
		if id == "" {
			http.Error(w, "OrderHeaderID bulunamadı", http.StatusBadRequest)
			return
		}

		rows, err := queries.GetOrderProductionItems(mssql, id)
		if err != nil {
			log.Printf("❌ SQL sorgu hatası: %v", err)
			http.Error(w, "Veritabanı hatası", http.StatusInternalServerError)
			return
		}
		defer rows.Close()

		list := make([]models.OrderProductionItem, 0, 100)

		for rows.Next() {
			var o models.OrderProductionItem
			if err := rows.Scan(
				&o.OrderHeaderID,
				&o.OrderLineID,
				&o.ItemTypeCode,
				&o.OldDim1,
				&o.OldDim3,
				&o.OldItemCode,
				&o.OldColor,
				&o.OldDim2,
				&o.OldDesc,
				&o.NewItemCode,
				&o.NewColor,
				&o.NewDim2,
				&o.NewDesc,
				&o.IsVariantMissing,
			); err != nil {
				log.Printf("⚠️ SCAN HATASI: %v", err)
				continue
			}
			list = append(list, o)
		}

		if err := rows.Err(); err != nil {
			log.Printf("⚠️ rows.Err(): %v", err)
		}

		if err := json.NewEncoder(w).Encode(list); err != nil {
			log.Printf("❌ encode error: %v", err)
		}
	})
}

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

		lookups, err := queries.GetOrderProductionLookupOptions(mssql)
		if err != nil {
			log.Printf("[OrderProductionCdItemLookupsRoute] lookup error: %v", err)
			http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
			return
		}

		if err := json.NewEncoder(w).Encode(lookups); err != nil {
			log.Printf("[OrderProductionCdItemLookupsRoute] encode error: %v", err)
		}
	})
}

// ======================================================
// 📌 OrderProductionInsertMissingRoute — eksik varyantları ekler
// ======================================================
func OrderProductionInsertMissingRoute(mssql *sql.DB) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

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

		id := mux.Vars(r)["id"]
		if id == "" {
			http.Error(w, "OrderHeaderID bulunamadı", http.StatusBadRequest)
			return
		}

		claims, _ := auth.GetClaimsFromContext(r.Context())
		username := ""
		if claims != nil {
			username = claims.Username
		}
		if username == "" {
			username = "system"
		}

		affected, err := queries.InsertMissingProductionVariants(mssql, id, username)
		if err != nil {
			log.Printf("❌ INSERT varyant hatası: %v", err)
			http.Error(w, "Veritabanı hatası", http.StatusInternalServerError)
			return
		}

		resp := map[string]any{
			"inserted": affected,
		}
		if err := json.NewEncoder(w).Encode(resp); err != nil {
			log.Printf("❌ encode error: %v", err)
		}
	})
}

// ======================================================
// OrderProductionValidateRoute - yeni model varyant kontrolu
// ======================================================
func OrderProductionValidateRoute(mssql *sql.DB) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")

		id := mux.Vars(r)["id"]
		if id == "" {
			http.Error(w, "OrderHeaderID bulunamadi", http.StatusBadRequest)
			return
		}

		var payload models.OrderProductionUpdatePayload
		if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
			http.Error(w, "Gecersiz istek", http.StatusBadRequest)
			return
		}
		if err := validateUpdateLines(payload.Lines); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		missing, err := buildMissingVariants(mssql, id, payload.Lines)
		if err != nil {
			writeDBError(w, http.StatusInternalServerError, "validate_missing_variants", id, "", len(payload.Lines), err)
			return
		}

		resp := map[string]any{
			"missingCount": len(missing),
			"missing":      missing,
		}
		if err := json.NewEncoder(w).Encode(resp); err != nil {
			log.Printf("❌ encode error: %v", err)
		}
	})
}

// ======================================================
// OrderProductionApplyRoute - yeni model varyant guncelleme
// ======================================================
func OrderProductionApplyRoute(mssql *sql.DB) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")

		id := mux.Vars(r)["id"]
		if id == "" {
			http.Error(w, "OrderHeaderID bulunamadi", http.StatusBadRequest)
			return
		}

		var payload models.OrderProductionUpdatePayload
		if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
			http.Error(w, "Gecersiz istek", http.StatusBadRequest)
			return
		}
		if err := validateUpdateLines(payload.Lines); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		missing, err := buildMissingVariants(mssql, id, payload.Lines)
		if err != nil {
			writeDBError(w, http.StatusInternalServerError, "apply_validate_missing_variants", id, "", len(payload.Lines), err)
			return
		}

		if len(missing) > 0 && !payload.InsertMissing {
			w.WriteHeader(http.StatusConflict)
			_ = json.NewEncoder(w).Encode(map[string]any{
				"missingCount": len(missing),
				"missing":      missing,
				"message":      "Eksik varyantlar var",
			})
			return
		}

		claims, _ := auth.GetClaimsFromContext(r.Context())
		username := ""
		if claims != nil {
			username = claims.Username
		}
		if strings.TrimSpace(username) == "" {
			username = "system"
		}

		tx, err := mssql.Begin()
		if err != nil {
			writeDBError(w, http.StatusInternalServerError, "begin_tx", id, username, len(payload.Lines), err)
			return
		}
		defer tx.Rollback()

		var inserted int64
		if payload.InsertMissing {
			cdItemByCode := buildCdItemDraftMap(payload.CdItems)
			inserted, err = queries.InsertMissingVariantsTx(tx, missing, username, cdItemByCode)
			if err != nil {
				writeDBError(w, http.StatusInternalServerError, "insert_missing_variants", id, username, len(missing), err)
				return
			}
		}

		if err := validateProductAttributes(payload.ProductAttributes); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		attributeAffected, err := queries.UpsertItemAttributesTx(tx, payload.ProductAttributes, username)
		if err != nil {
			writeDBError(w, http.StatusInternalServerError, "upsert_item_attributes", id, username, len(payload.ProductAttributes), err)
			return
		}

		updated, err := queries.UpdateOrderLinesTx(tx, id, payload.Lines, username)
		if err != nil {
			writeDBError(w, http.StatusInternalServerError, "update_order_lines", id, username, len(payload.Lines), err)
			return
		}

		if err := tx.Commit(); err != nil {
			writeDBError(w, http.StatusInternalServerError, "commit_tx", id, username, len(payload.Lines), err)
			return
		}

		resp := map[string]any{
			"updated":           updated,
			"inserted":          inserted,
			"attributeUpserted": attributeAffected,
		}
		if err := json.NewEncoder(w).Encode(resp); err != nil {
			log.Printf("❌ encode error: %v", err)
		}
	})
}

func validateProductAttributes(attrs []models.OrderProductionItemAttributeRow) error {
	for _, a := range attrs {
		if strings.TrimSpace(a.ItemCode) == "" {
			return errors.New("Urun ozellikleri icin ItemCode zorunlu")
		}
		if !baggiModelCodeRegex.MatchString(strings.ToUpper(strings.TrimSpace(a.ItemCode))) {
			return errors.New("Girdiginiz kod BAGGI kod sistemine uyumlu degil. Format: X999-XXX99999")
		}
		if a.ItemTypeCode <= 0 {
			return errors.New("Urun ozellikleri icin ItemTypeCode zorunlu")
		}
		if a.AttributeTypeCode <= 0 {
			return errors.New("Urun ozellikleri icin AttributeTypeCode zorunlu")
		}
		if strings.TrimSpace(a.AttributeCode) == "" {
			return errors.New("Urun ozellikleri icin AttributeCode zorunlu")
		}
	}
	return nil
}

func buildCdItemDraftMap(list []models.OrderProductionCdItemDraft) map[string]models.OrderProductionCdItemDraft {
	out := make(map[string]models.OrderProductionCdItemDraft, len(list))
	for _, item := range list {
		code := strings.ToUpper(strings.TrimSpace(item.ItemCode))
		if code == "" {
			continue
		}
		item.ItemCode = code
		if item.ItemTypeCode == 0 {
			item.ItemTypeCode = 1
		}
		key := queries.NormalizeCdItemMapKey(item.ItemTypeCode, item.ItemCode)
		out[key] = item
	}
	return out
}

func buildMissingVariants(mssql *sql.DB, orderHeaderID string, lines []models.OrderProductionUpdateLine) ([]models.OrderProductionMissingVariant, error) {
	missing := make([]models.OrderProductionMissingVariant, 0)

	for _, line := range lines {
		lineID := strings.TrimSpace(line.OrderLineID)
		newItem := strings.TrimSpace(line.NewItemCode)
		newColor := strings.TrimSpace(line.NewColor)
		newDim2 := strings.TrimSpace(line.NewDim2)

		if lineID == "" || newItem == "" {
			continue
		}

		itemTypeCode, dim1, _, dim3, err := queries.GetOrderLineDims(mssql, orderHeaderID, lineID)
		if err != nil {
			return nil, err
		}

		exists, err := queries.VariantExists(mssql, itemTypeCode, newItem, newColor, dim1, newDim2, dim3)
		if err != nil {
			return nil, err
		}
		if !exists {
			missing = append(missing, models.OrderProductionMissingVariant{
				OrderLineID:  lineID,
				ItemTypeCode: itemTypeCode,
				ItemCode:     newItem,
				ColorCode:    newColor,
				ItemDim1Code: dim1,
				ItemDim2Code: newDim2,
				ItemDim3Code: dim3,
			})
		}
	}

	return missing, nil
}

func validateUpdateLines(lines []models.OrderProductionUpdateLine) error {
	for _, line := range lines {
		if strings.TrimSpace(line.OrderLineID) == "" {
			return errors.New("OrderLineID zorunlu")
		}
		code := strings.ToUpper(strings.TrimSpace(line.NewItemCode))
		if code == "" {
			return errors.New("Yeni urun kodu zorunlu")
		}
		if !baggiModelCodeRegex.MatchString(code) {
			return errors.New("Girdiginiz kod BAGGI kod sistemine uyumlu degil. Format: X999-XXX99999")
		}
	}
	return nil
}

func writeDBError(w http.ResponseWriter, status int, step string, orderHeaderID string, username string, lineCount int, err error) {
	var sqlErr mssql.Error
	if errors.As(err, &sqlErr) {
		log.Printf(
			"❌ SQL error step=%s orderHeaderID=%s user=%s lineCount=%d number=%d state=%d class=%d server=%s proc=%s line=%d message=%s",
			step, orderHeaderID, username, lineCount,
			sqlErr.Number, sqlErr.State, sqlErr.Class, sqlErr.ServerName, sqlErr.ProcName, sqlErr.LineNo, sqlErr.Message,
		)
	} else {
		log.Printf(
			"❌ DB error step=%s orderHeaderID=%s user=%s lineCount=%d err=%v",
			step, orderHeaderID, username, lineCount, err,
		)
	}

	w.WriteHeader(status)
	_ = json.NewEncoder(w).Encode(map[string]any{
		"message": "Veritabani hatasi",
		"step":    step,
		"detail":  err.Error(),
	})
}
