package user

import (
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"math"
	"net/http"
	"time"

	"github.com/go-chi/chi/v5"

	"crussell/db"
	"github.com/jackc/pgx/v5"
	"crussell/internal/validators"
)

type CustomerRelationship struct {
	TotalSpend     float64      `json:"totalSpend"`
	TotalSaved     float64      `json:"totalSaved"`
	TotalTips      float64      `json:"totalTips"`
	TotalVisits    int          `json:"totalVisits"`
	CustomerFor    string       `json:"customerFor"`
	FirstVisitDate *string      `json:"firstVisitDate,omitempty"`
	LastVisitDate  *string      `json:"lastVisitDate,omitempty"`
	TopServices    []TopService `json:"topServices"`
}

type TopService struct {
	Name  string `json:"name"`
	Count int    `json:"count"`
}

// GET /api/admin/users/{id}/relationship
func GetCustomerRelationshipHandler(w http.ResponseWriter, r *http.Request) {
	userID := chi.URLParam(r, "id")
	if userID == "" || !validators.IsValidID(userID) {
		http.Error(w, "user not found", http.StatusNotFound)
		return
	}

	var result CustomerRelationship
	var firstVisit sql.NullTime
	var lastVisit sql.NullTime
	var exists bool

	err := db.Conn.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM users WHERE id = $1)`, userID).Scan(&exists)
	if err != nil {
		log.Printf("Failed to check user existence for %s: %v", userID, err)
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return
	}
	if !exists {
		http.Error(w, "user not found", http.StatusNotFound)
		return
	}

	err = db.Conn.QueryRow(r.Context(), `
		SELECT
			COALESCE(SUM(p.amount) FILTER (WHERE p.status = 'completed' AND p.payment_type IN ('full','partial','balance','deposit') AND p.payment_method != 'discount'), 0),
			COALESCE(SUM(p.amount) FILTER (WHERE p.status = 'completed' AND p.payment_type IN ('full','partial','balance','deposit') AND p.payment_method = 'discount'), 0),
			COALESCE(SUM(p.amount) FILTER (WHERE p.status = 'completed' AND p.payment_type = 'tip'), 0),
			COUNT(DISTINCT b.id) FILTER (WHERE b.status = 'completed'),
			MIN(b.start_time) FILTER (WHERE b.status = 'completed'),
			MAX(b.start_time) FILTER (WHERE b.status = 'completed')
		FROM bookings b
		LEFT JOIN payments p ON p.booking_id = b.id
		WHERE b.user_id = $1
	`, userID).Scan(&result.TotalSpend, &result.TotalSaved, &result.TotalTips, &result.TotalVisits, &firstVisit, &lastVisit)
	if err != nil && !errors.Is(err, pgx.ErrNoRows) {
		log.Printf("Failed to get customer relationship data for user %s: %v", userID, err)
	}

	if firstVisit.Valid {
		firstVisitStr := firstVisit.Time.Format("2006-01-02T15:04:05Z07:00")
		result.FirstVisitDate = &firstVisitStr

		days := int(time.Since(firstVisit.Time).Hours() / 24)
		switch {
		case days < 7:
			result.CustomerFor = formatPlural(days, "day")
		case days < 30:
			result.CustomerFor = formatPlural(int(math.Round(float64(days)/7)), "week")
		case days < 365:
			result.CustomerFor = formatPlural(int(math.Round(float64(days)/30)), "month")
		default:
			result.CustomerFor = formatPlural(int(math.Round(float64(days)/365.25)), "year")
		}
	}

	if lastVisit.Valid {
		lastVisitStr := lastVisit.Time.Format("2006-01-02T15:04:05Z07:00")
		result.LastVisitDate = &lastVisitStr
	}

	rows, err := db.Conn.Query(r.Context(), `
		SELECT name, cnt FROM (
			SELECT s.name, COUNT(*) as cnt
			FROM booking_services bsvc
			JOIN bookings b ON bsvc.booking_id = b.id
			JOIN services s ON bsvc.service_id = s.id
			WHERE b.user_id = $1 AND b.status = 'completed'
			GROUP BY s.name
			UNION ALL
			SELECT cs.name, COUNT(*) as cnt
			FROM booking_custom_services bcs
			JOIN bookings b ON bcs.booking_id = b.id
			JOIN custom_services cs ON bcs.custom_service_id = cs.id
			WHERE b.user_id = $1 AND b.status = 'completed'
			GROUP BY cs.name
		) combined
		ORDER BY cnt DESC
		LIMIT 5
	`, userID)
	if err != nil && !errors.Is(err, pgx.ErrNoRows) {
		log.Printf("Failed to get top services for user %s: %v", userID, err)
	} else {
		defer rows.Close()
		for rows.Next() {
			var ts TopService
			if err := rows.Scan(&ts.Name, &ts.Count); err != nil {
				log.Printf("Failed to scan top service: %v", err)
				continue
			}
			result.TopServices = append(result.TopServices, ts)
		}
		if err := rows.Err(); err != nil {
			log.Printf("Row iteration error: %v", err)
			http.Error(w, "Internal server error", http.StatusInternalServerError)
			return
		}
	}

	if result.TopServices == nil {
		result.TopServices = []TopService{}
	}


	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(result); err != nil {
		log.Printf("Failed to encode customer relationship response: %v", err)
	}
}

func formatPlural(n int, unit string) string {
	if n == 1 {
		return fmt.Sprintf("1 %s", unit)
	}
	return fmt.Sprintf("%d %ss", n, unit)
}
