package handler

import (
	"context"
	"errors"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/gofrs/uuid/v5"
	"github.com/saveinmed/backend-go/internal/domain"
	"github.com/saveinmed/backend-go/internal/http/middleware"
)

// --- Request DTOs ---

type createUserRequest struct {
	CompanyID uuid.UUID `json:"company_id"`
	Role      string    `json:"role"`
	Name      string    `json:"name"`
	Username  string    `json:"username"`
	Email     string    `json:"email"`
	Password  string    `json:"password"`
}

type registerAuthRequest struct {
	CompanyID *uuid.UUID             `json:"company_id,omitempty"`
	Company   *registerCompanyTarget `json:"company,omitempty"`
	Role      string                 `json:"role"`
	Name      string                 `json:"name"`
	Username  string                 `json:"username"`
	Email     string                 `json:"email"`
	Password  string                 `json:"password"`
}

type registerCompanyTarget struct {
	ID            uuid.UUID `json:"id,omitempty"`
	Category      string    `json:"category"`
	CNPJ          string    `json:"cnpj"`
	CorporateName string    `json:"corporate_name"`
	LicenseNumber string    `json:"license_number"`
	Latitude      float64   `json:"latitude"`
	Longitude     float64   `json:"longitude"`
	City          string    `json:"city"`
	State         string    `json:"state"`
}

type loginRequest struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

type forgotPasswordRequest struct {
	Email string `json:"email"`
}

type resetPasswordRequest struct {
	Token    string `json:"token"`
	Password string `json:"password"`
}

type verifyEmailRequest struct {
	Token string `json:"token"`
}

type authResponse struct {
	Token     string    `json:"token"`
	ExpiresAt time.Time `json:"expires_at"`
}

type messageResponse struct {
	Message string `json:"message"`
}

type resetTokenResponse struct {
	Message    string     `json:"message"`
	ResetToken string     `json:"reset_token,omitempty"`
	ExpiresAt  *time.Time `json:"expires_at,omitempty"`
}

type inventoryAdjustRequest struct {
	ProductID uuid.UUID `json:"product_id"`
	Delta     int64     `json:"delta"`
	Reason    string    `json:"reason"`
}

type addCartItemRequest struct {
	ProductID uuid.UUID `json:"product_id"`
	Quantity  int64     `json:"quantity"`
}

type createReviewRequest struct {
	OrderID uuid.UUID `json:"order_id"`
	Rating  int       `json:"rating"`
	Comment string    `json:"comment"`
}

type updateUserRequest struct {
	CompanyID *uuid.UUID `json:"company_id,omitempty"`
	Role      *string    `json:"role,omitempty"`
	Name      *string    `json:"name,omitempty"`
	Username  *string    `json:"username,omitempty"`
	Email     *string    `json:"email,omitempty"`
	Password  *string    `json:"password,omitempty"`
}

type requester struct {
	Role      string
	CompanyID *uuid.UUID
}

type registerCompanyRequest struct {
	Category      string  `json:"category"`
	CNPJ          string  `json:"cnpj"`
	CorporateName string  `json:"corporate_name"`
	LicenseNumber string  `json:"license_number"`
	Latitude      float64 `json:"latitude"`
	Longitude     float64 `json:"longitude"`
	City          string  `json:"city"`
	State         string  `json:"state"`
}

type updateCompanyRequest struct {
	Category      *string  `json:"category,omitempty"`
	CNPJ          *string  `json:"cnpj,omitempty"`
	CorporateName *string  `json:"corporate_name,omitempty"`
	LicenseNumber *string  `json:"license_number,omitempty"`
	IsVerified    *bool    `json:"is_verified,omitempty"`
	Latitude      *float64 `json:"latitude,omitempty"`
	Longitude     *float64 `json:"longitude,omitempty"`
	City          *string  `json:"city,omitempty"`
	State         *string  `json:"state,omitempty"`
}

type registerProductRequest struct {
	SellerID    uuid.UUID `json:"seller_id"`
	Name        string    `json:"name"`
	Description string    `json:"description"`
	Batch       string    `json:"batch"`
	ExpiresAt   time.Time `json:"expires_at"`
	PriceCents  int64     `json:"price_cents"`
	Stock       int64     `json:"stock"`
}

type updateProductRequest struct {
	SellerID    *uuid.UUID `json:"seller_id,omitempty"`
	Name        *string    `json:"name,omitempty"`
	Description *string    `json:"description,omitempty"`
	Batch       *string    `json:"batch,omitempty"`
	ExpiresAt   *time.Time `json:"expires_at,omitempty"`
	PriceCents  *int64     `json:"price_cents,omitempty"`
	Stock       *int64     `json:"stock,omitempty"`
}

type createOrderRequest struct {
	BuyerID  uuid.UUID              `json:"buyer_id"`
	SellerID uuid.UUID              `json:"seller_id"`
	Items    []domain.OrderItem     `json:"items"`
	Shipping domain.ShippingAddress `json:"shipping"`
}

type createShipmentRequest struct {
	OrderID          uuid.UUID `json:"order_id"`
	Carrier          string    `json:"carrier"`
	TrackingCode     string    `json:"tracking_code"`
	ExternalTracking string    `json:"external_tracking"`
}

type updateStatusRequest struct {
	Status string `json:"status"`
}

type shippingMethodRequest struct {
	Type                       string  `json:"type"`
	Active                     bool    `json:"active"`
	PreparationMinutes         int     `json:"preparation_minutes"`
	MaxRadiusKm                float64 `json:"max_radius_km"`
	MinFeeCents                int64   `json:"min_fee_cents"`
	PricePerKmCents            int64   `json:"price_per_km_cents"`
	FreeShippingThresholdCents *int64  `json:"free_shipping_threshold_cents,omitempty"`
	PickupAddress              string  `json:"pickup_address,omitempty"`
	PickupHours                string  `json:"pickup_hours,omitempty"`
}

type shippingSettingsRequest struct {
	Methods []shippingMethodRequest `json:"methods"`
}

type shippingCalculateRequest struct {
	VendorID       uuid.UUID  `json:"vendor_id"`
	CartTotalCents int64      `json:"cart_total_cents"`
	BuyerLatitude  *float64   `json:"buyer_latitude,omitempty"`
	BuyerLongitude *float64   `json:"buyer_longitude,omitempty"`
	AddressID      *uuid.UUID `json:"address_id,omitempty"`
	PostalCode     string     `json:"postal_code,omitempty"`
}

// --- Utility Functions ---

func writeJSON(w http.ResponseWriter, status int, v any) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	_ = json.NewEncoder(w).Encode(v)
}

func writeError(w http.ResponseWriter, status int, err error) {
	writeJSON(w, status, map[string]string{"error": err.Error()})
}

func decodeJSON(ctx context.Context, r *http.Request, v any) error {
	ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
	defer cancel()

	dec := json.NewDecoder(r.Body)
	dec.DisallowUnknownFields()
	if err := dec.Decode(v); err != nil {
		return err
	}
	return ctx.Err()
}

func parseUUIDFromPath(path string) (uuid.UUID, error) {
	parts := splitPath(path)
	for i := len(parts) - 1; i >= 0; i-- {
		if id, err := uuid.FromString(parts[i]); err == nil {
			return id, nil
		}
	}
	return uuid.UUID{}, errors.New("missing resource id")
}

func splitPath(p string) []string {
	var parts []string
	start := 0
	for i := 0; i < len(p); i++ {
		if p[i] == '/' {
			if i > start {
				parts = append(parts, p[start:i])
			}
			start = i + 1
		}
	}
	if start < len(p) {
		parts = append(parts, p[start:])
	}
	return parts
}

func isValidStatus(status string) bool {
	switch domain.OrderStatus(status) {
	case domain.OrderStatusPending, domain.OrderStatusPaid, domain.OrderStatusInvoiced, domain.OrderStatusDelivered:
		return true
	default:
		return false
	}
}

func parsePagination(r *http.Request) (int, int) {
	page := 1
	pageSize := 20

	if v := r.URL.Query().Get("page"); v != "" {
		if p, err := strconv.Atoi(v); err == nil && p > 0 {
			page = p
		}
	}

	if v := r.URL.Query().Get("page_size"); v != "" {
		if ps, err := strconv.Atoi(v); err == nil && ps > 0 {
			pageSize = ps
		}
	}

	return page, pageSize
}

func getRequester(r *http.Request) (requester, error) {
	if claims, ok := middleware.GetClaims(r.Context()); ok {
		return requester{Role: claims.Role, CompanyID: claims.CompanyID}, nil
	}
	role := r.Header.Get("X-User-Role")
	if role == "" {
		role = "Admin"
	}

	var companyID *uuid.UUID
	if cid := r.Header.Get("X-Company-ID"); cid != "" {
		id, err := uuid.FromString(cid)
		if err != nil {
			return requester{}, errors.New("invalid X-Company-ID header")
		}
		companyID = &id
	}

	return requester{Role: role, CompanyID: companyID}, nil
}

func parseBearerToken(r *http.Request) (string, error) {
	authHeader := r.Header.Get("Authorization")
	if authHeader == "" {
		return "", errors.New("missing Authorization header")
	}
	parts := strings.SplitN(authHeader, " ", 2)
	if len(parts) != 2 || !strings.EqualFold(parts[0], "bearer") {
		return "", errors.New("invalid Authorization header")
	}
	token := strings.TrimSpace(parts[1])
	if token == "" {
		return "", errors.New("token is required")
	}
	return token, nil
}
