package today

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

	"crussell/db"
)

type ServiceInfo struct {
	ServiceName        *string  `json:"service_name,omitempty"`
	ServiceDescription *string  `json:"service_description,omitempty"`
	Price              *float64 `json:"price,omitempty"`
	DurationMinutes    *int     `json:"duration_minutes,omitempty"`
}

type UserInfo struct {
	ID            string  `json:"id"`
	FullName      string  `json:"full_name"`
	Phone         *string `json:"phone,omitempty"`
	Email         *string `json:"email,omitempty"`
	ProfilePicURL *string `json:"profile_pic_url,omitempty"`
}

type AppointmentInfo struct {
	ID              string        `json:"id"`
	StartTime       time.Time     `json:"start_time"`
	Status          string        `json:"status"`
	Notes           *string       `json:"notes,omitempty"`
	User            *UserInfo     `json:"user,omitempty"`
	Services        []ServiceInfo `json:"services"`
	DurationMinutes int           `json:"duration_minutes"`
	TotalAmount     float64       `json:"total_amount"`
}

type CurrentNextResponse struct {
	Current    *AppointmentInfo `json:"current"`
	Next       *AppointmentInfo `json:"next"`
	ClosingTime *string         `json:"closing_time,omitempty"` // "HH:MM" format
}

// GET /api/admin/today/current-next
func GetCurrentAndNextHandler(w http.ResponseWriter, r *http.Request) {
	now := time.Now()

	// Auto-transition confirmed bookings that have started but not ended to in_progress
	_, err := db.DB.Exec(r.Context(), `
		UPDATE bookings 
		SET status = 'in_progress'
		WHERE status = 'confirmed'
		  AND start_time <= $1
		  AND (
		    start_time + (
		      COALESCE(
		        (SELECT SUM(COALESCE(bs.override_duration_minutes, s.duration_minutes)) 
		         FROM booking_services bs JOIN services s ON bs.service_id = s.id 
		         WHERE bs.booking_id = bookings.id),
		        0
		      ) || ' minutes'
		    )::interval
		  ) > $1
	`, now)
	if err != nil {
		log.Printf("Failed to auto-transition bookings to in_progress: %v", err)
	}

	// Auto-transition in_progress bookings that have ended to completed
	_, err = db.DB.Exec(r.Context(), `
		UPDATE bookings 
		SET status = 'completed'
		WHERE status = 'in_progress'
		  AND (
		    start_time + (
		      COALESCE(
		        (SELECT SUM(COALESCE(bs.override_duration_minutes, s.duration_minutes)) 
		         FROM booking_services bs JOIN services s ON bs.service_id = s.id 
		         WHERE bs.booking_id = bookings.id),
		        0
		      ) || ' minutes'
		    )::interval
		  ) <= $1
	`, now)
	if err != nil {
		log.Printf("Failed to auto-transition bookings to completed: %v", err)
	}

	var current *AppointmentInfo
	var next *AppointmentInfo

	// Step 1: Try to find current in-progress appointment
	currentBooking, err := fetchAppointment(r, `
		SELECT b.id, b.start_time, b.status, b.notes, b.user_id
		FROM bookings b
		WHERE b.status = 'in_progress'
		ORDER BY b.start_time ASC
		LIMIT 1
	`)

	if err != nil && err != sql.ErrNoRows {
		log.Printf("Error querying current appointment: %v", err)
		// Don't fail, continue to find next
	}

	if err == nil && currentBooking != nil {
		current = currentBooking

		// Also fetch the appointment AFTER this one for free time calculation
		nextBooking, err := fetchAppointment(r, `
			SELECT b.id, b.start_time, b.status, b.notes, b.user_id
			FROM bookings b
			WHERE b.start_time > $1
			  AND (b.status = 'confirmed' OR b.status = 'pending' OR b.status = 'in_progress')
			ORDER BY b.start_time ASC
			LIMIT 1
		`, currentBooking.StartTime)

		if err == nil && nextBooking != nil {
			next = nextBooking
		}
	} else {
		// No current in-progress appointment, find the next upcoming one
		nextBooking, err := fetchAppointment(r, `
			SELECT b.id, b.start_time, b.status, b.notes, b.user_id
			FROM bookings b
			WHERE b.start_time >= $1
			  AND (b.status = 'confirmed' OR b.status = 'pending')
			ORDER BY b.start_time ASC
			LIMIT 1
		`, now)

		if err != nil && err != sql.ErrNoRows {
			log.Printf("Error querying next appointment: %v", err)
		}

		if err == nil && nextBooking != nil {
			current = nextBooking

			// Fetch the one after for free time calculation
			afterNext, err := fetchAppointment(r, `
				SELECT b.id, b.start_time, b.status, b.notes, b.user_id
				FROM bookings b
				WHERE b.start_time > $1
				  AND (b.status = 'confirmed' OR b.status = 'pending' OR b.status = 'in_progress')
				ORDER BY b.start_time ASC
				LIMIT 1
			`, nextBooking.StartTime)

			if err == nil && afterNext != nil {
				next = afterNext
			}
		}
	}

	response := CurrentNextResponse{
		Current: current,
		Next:    next,
	}

	weekday := int(now.Weekday())
	if weekday == 0 {
		weekday = 7
	}
	var closingTime sql.NullString
	_ = db.DB.QueryRow(r.Context(), `
		SELECT end_time::text FROM working_hours WHERE weekday = $1 AND is_open = true
	`, weekday).Scan(&closingTime)
	if closingTime.Valid {
		response.ClosingTime = &closingTime.String
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(response); err != nil {
		log.Printf("Failed to encode current/next response: %v", err)
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return
	}
}

// Helper function to fetch a single appointment with all details
func fetchAppointment(r *http.Request, query string, args ...interface{}) (*AppointmentInfo, error) {
	var bookingID string
	var startTime time.Time
	var status string
	var notes sql.NullString
	var userID string

	err := db.DB.QueryRow(r.Context(), query, args...).Scan(
		&bookingID, &startTime, &status, &notes, &userID,
	)
	if err != nil {
		return nil, err
	}

	appointment := &AppointmentInfo{
		ID:        bookingID,
		StartTime: startTime,
		Status:    status,
	}

	if notes.Valid {
		appointment.Notes = &notes.String
	}

	// Fetch user info
	var user UserInfo
	var phone sql.NullString
	var email sql.NullString
	var profilePicURL sql.NullString

	err = db.DB.QueryRow(r.Context(), `
		SELECT id, fn, phone, email, profile_pic_url
		FROM users
		WHERE id = $1
	`, userID).Scan(&user.ID, &user.FullName, &phone, &email, &profilePicURL)

	if err == nil {
		if phone.Valid {
			user.Phone = &phone.String
		}
		if profilePicURL.Valid {
			user.ProfilePicURL = &profilePicURL.String
		}
		if email.Valid {
			user.Email = &email.String
		}
		appointment.User = &user
	} else {
		log.Printf("Failed to fetch user %s: %v", userID, err)
	}

	// Fetch services
	serviceRows, err := db.DB.Query(r.Context(), `
		SELECT
			s.name,
			s.description,
			COALESCE(bs.override_price, s.price) as price,
			COALESCE(bs.override_duration_minutes, s.duration_minutes) as duration_minutes
		FROM booking_services bs
		LEFT JOIN services s ON bs.service_id = s.id
		WHERE bs.booking_id = $1
		ORDER BY s.name
	`, bookingID)

	if err != nil {
		log.Printf("Failed to fetch services for booking %s: %v", bookingID, err)
		return appointment, nil
	}
	defer serviceRows.Close()

	var totalAmount float64
	var totalDuration int

	for serviceRows.Next() {
		var service ServiceInfo
		var name string
		var description sql.NullString
		var price float64
		var duration int

		if err := serviceRows.Scan(&name, &description, &price, &duration); err != nil {
			log.Printf("Failed to scan service: %v", err)
			continue
		}

		service.ServiceName = &name
		if description.Valid {
			service.ServiceDescription = &description.String
		}
		service.Price = &price
		service.DurationMinutes = &duration

		totalAmount += price
		totalDuration += duration

		appointment.Services = append(appointment.Services, service)
	}

	appointment.TotalAmount = totalAmount
	appointment.DurationMinutes = totalDuration

	return appointment, nil
}

type TodayAppointment struct {
	ID              string   `json:"id"`
	StartTime       string   `json:"start_time"`
	Status          string   `json:"status"`
	UserName        string   `json:"user_name"`
	UserID          string   `json:"user_id"`
	Services        []string `json:"services"`
	DurationMinutes int      `json:"duration_minutes"`
}

type TodayAppointmentsResponse struct {
	Appointments []TodayAppointment `json:"appointments"`
}

// GET /api/admin/today/appointments
func GetTodayAppointmentsHandler(w http.ResponseWriter, r *http.Request) {
	now := time.Now()

	// Auto-transition confirmed bookings that have started but not ended to in_progress
	_, err := db.DB.Exec(r.Context(), `
		UPDATE bookings 
		SET status = 'in_progress'
		WHERE status = 'confirmed'
		  AND start_time <= $1
		  AND (
		    start_time + (
		      COALESCE(
		        (SELECT SUM(COALESCE(bs.override_duration_minutes, s.duration_minutes)) 
		         FROM booking_services bs JOIN services s ON bs.service_id = s.id 
		         WHERE bs.booking_id = bookings.id),
		        0
		      ) || ' minutes'
		    )::interval
		  ) > $1
	`, now)
	if err != nil {
		log.Printf("Failed to auto-transition bookings to in_progress: %v", err)
	}

	// Auto-transition in_progress bookings that have ended to completed
	_, err = db.DB.Exec(r.Context(), `
		UPDATE bookings 
		SET status = 'completed'
		WHERE status = 'in_progress'
		  AND (
		    start_time + (
		      COALESCE(
		        (SELECT SUM(COALESCE(bs.override_duration_minutes, s.duration_minutes)) 
		         FROM booking_services bs JOIN services s ON bs.service_id = s.id 
		         WHERE bs.booking_id = bookings.id),
		        0
		      ) || ' minutes'
		    )::interval
		  ) <= $1
	`, now)
	if err != nil {
		log.Printf("Failed to auto-transition bookings to completed: %v", err)
	}

	todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
	todayEnd := todayStart.Add(24 * time.Hour)

	// Fetch all bookings for today
	rows, err := db.DB.Query(r.Context(), `
		SELECT
			b.id,
			b.start_time,
			b.status,
			u.fn as user_name,
			u.id as user_id
		FROM bookings b
		LEFT JOIN users u ON b.user_id = u.id
		WHERE b.start_time >= $1
		  AND b.start_time < $2
		ORDER BY b.start_time ASC
	`, todayStart, todayEnd)

	if err != nil {
		log.Printf("Failed to fetch today's appointments: %v", err)
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return
	}
	defer rows.Close()

	var appointments []TodayAppointment

	for rows.Next() {
		var apt TodayAppointment
		var startTime time.Time

		err := rows.Scan(
			&apt.ID,
			&startTime,
			&apt.Status,
			&apt.UserName,
			&apt.UserID,
		)
		if err != nil {
			log.Printf("Failed to scan appointment row: %v", err)
			continue
		}

		apt.StartTime = startTime.Format(time.RFC3339)

		// Fetch services for this booking
		serviceRows, err := db.DB.Query(r.Context(), `
			SELECT
				s.name,
				COALESCE(bs.override_duration_minutes, s.duration_minutes) as duration_minutes
			FROM booking_services bs
			LEFT JOIN services s ON bs.service_id = s.id
			WHERE bs.booking_id = $1
			ORDER BY s.name
		`, apt.ID)

		if err != nil {
			log.Printf("Failed to fetch services for booking %s: %v", apt.ID, err)
			continue
		}

		var services []string
		var totalDuration int

		for serviceRows.Next() {
			var serviceName string
			var duration int

			if err := serviceRows.Scan(&serviceName, &duration); err != nil {
				log.Printf("Failed to scan service: %v", err)
				continue
			}

			services = append(services, serviceName)
			totalDuration += duration
		}
		serviceRows.Close()

		apt.Services = services
		apt.DurationMinutes = totalDuration

		appointments = append(appointments, apt)
	}

	if appointments == nil {
		appointments = []TodayAppointment{}
	}

	response := TodayAppointmentsResponse{
		Appointments: appointments,
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(response); err != nil {
		log.Printf("Failed to encode today's appointments response: %v", err)
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return
	}
}

type PendingApproval struct {
	ID              string   `json:"id"`
	StartTime       string   `json:"start_time"`
	UserID          string   `json:"user_id"`
	UserName        string   `json:"user_name"`
	Services        []string `json:"services"`
	DurationMinutes int      `json:"duration_minutes"`
	CreatedAt       string   `json:"created_at"`
}

type PendingApprovalsResponse struct {
	Approvals []PendingApproval `json:"approvals"`
}

// GET /api/admin/today/pending-approvals
func GetPendingApprovalsHandler(w http.ResponseWriter, r *http.Request) {
	rows, err := db.DB.Query(r.Context(), `
		SELECT
			b.id,
			b.start_time,
			b.created_at,
			u.id as user_id,
			u.fn as user_name
		FROM bookings b
		LEFT JOIN users u ON b.user_id = u.id
		WHERE b.status = 'pending'
		ORDER BY b.created_at ASC
	`)

	if err != nil {
		log.Printf("Failed to fetch pending approvals: %v", err)
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return
	}
	defer rows.Close()

	var approvals []PendingApproval

	for rows.Next() {
		var apt PendingApproval
		var startTime time.Time
		var createdAt time.Time

		err := rows.Scan(
			&apt.ID,
			&startTime,
			&createdAt,
			&apt.UserID,
			&apt.UserName,
		)
		if err != nil {
			log.Printf("Failed to scan pending approval row: %v", err)
			continue
		}

		apt.StartTime = startTime.Format(time.RFC3339)
		apt.CreatedAt = createdAt.Format(time.RFC3339)

		// Fetch services
		serviceRows, err := db.DB.Query(r.Context(), `
			SELECT
				s.name,
				COALESCE(bs.override_duration_minutes, s.duration_minutes) as duration_minutes
			FROM booking_services bs
			LEFT JOIN services s ON bs.service_id = s.id
			WHERE bs.booking_id = $1
			ORDER BY s.name
		`, apt.ID)

		if err != nil {
			log.Printf("Failed to fetch services for booking %s: %v", apt.ID, err)
			continue
		}

		var services []string
		var totalDuration int

		for serviceRows.Next() {
			var name string
			var duration int

			if err := serviceRows.Scan(&name, &duration); err != nil {
				log.Printf("Failed to scan service: %v", err)
				continue
			}

			services = append(services, name)
			totalDuration += duration
		}
		serviceRows.Close()

		apt.Services = services
		apt.DurationMinutes = totalDuration

		approvals = append(approvals, apt)
	}

	if approvals == nil {
		approvals = []PendingApproval{}
	}

	response := PendingApprovalsResponse{
		Approvals: approvals,
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(response); err != nil {
		log.Printf("Failed to encode pending approvals response: %v", err)
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return
	}
}
