package handlers

import (
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/patel-mann/poll-system/app/internal/models"
	"github.com/patel-mann/poll-system/app/internal/utils"
)

// PaginationInfo holds pagination metadata
type PaginationInfo struct {
	CurrentPage  int
	TotalPages   int
	TotalRecords int
	PageSize     int
	HasPrevious  bool
	HasNext      bool
	StartRecord  int
	EndRecord    int
	PreviousPage int
	NextPage     int
	FirstPage    int
	LastPage     int
	PageNumbers  []PageNumber
}

type PageNumber struct {
	Number    int
	IsCurrent bool
}

// AddressWithDetails extends AddressDatabase with appointment and user info
type AddressWithDetails struct {
	models.AddressDatabase
	UserID			*int
	UserName         string
	UserEmail        string
	AppointmentDate  string
	AppointmentTime  string
}

func AddressHandler(w http.ResponseWriter, r *http.Request) {
	// Get pagination parameters from query string
	pageStr := r.URL.Query().Get("page")
	pageSizeStr := r.URL.Query().Get("pageSize")
	username,_ := models.GetCurrentUserName(r)


	page := 1
	pageSize := 20
	if pageStr != "" {
		if p, err := strconv.Atoi(pageStr); err == nil && p > 0 {
			page = p
		}
	}
	if pageSizeStr != "" {
		if ps, err := strconv.Atoi(pageSizeStr); err == nil && ps > 0 && ps <= 100 {
			pageSize = ps
		}
	}
	offset := (page - 1) * pageSize

	// Get total count
	var totalRecords int
	err := models.DB.QueryRow(`SELECT COUNT(*) FROM "address_database"`).Scan(&totalRecords)
	if err != nil {
		log.Println("Count query error:", err)
		http.Error(w, "Database error", http.StatusInternalServerError)
		return
	}
	totalPages := (totalRecords + pageSize - 1) / pageSize
	if totalPages == 0 {
		totalPages = 1
	}
	if page > totalPages {
		page = totalPages
		offset = (page - 1) * pageSize
	}

	// Query addresses with appointment + user info
	rows, err := models.DB.Query(`
		SELECT 
			a.address_id,
			a.address, 
			a.street_name, 
			a.street_type,
			a.street_quadrant, 
			a.house_number,
			COALESCE(a.house_alpha, '') as house_alpha,
			a.longitude, 
			a.latitude,
			a.visited_validated,
			a.created_at,
			a.updated_at,
			CASE 
				WHEN ap.sched_id IS NOT NULL THEN true 
				ELSE false 
			END as assigned,
			ap.user_id,
			COALESCE(u.first_name || ' ' || u.last_name, '') as user_name,
			COALESCE(u.email, '') as user_email,
			COALESCE(ap.appointment_date::text, '') as appointment_date,
			COALESCE(ap.appointment_time::text, '') as appointment_time
		FROM address_database a
		LEFT JOIN appointment ap ON a.address_id = ap.address_id
		LEFT JOIN users u ON ap.user_id = u.user_id
		WHERE a.street_quadrant = 'ne'
		ORDER BY a.address_id
		LIMIT $1 OFFSET $2
	`, pageSize, offset)
	if err != nil {
		log.Println("Query error:", err)
		http.Error(w, "Database error", http.StatusInternalServerError)
		return
	}
	defer rows.Close()

	var addresses []AddressWithDetails
	for rows.Next() {
		var a AddressWithDetails
		var houseAlpha string
		err := rows.Scan(
			&a.AddressID,
			&a.Address,
			&a.StreetName,
			&a.StreetType,
			&a.StreetQuadrant,
			&a.HouseNumber,
			&houseAlpha,
			&a.Longitude,
			&a.Latitude,
			&a.VisitedValidated,
			&a.CreatedAt,
			&a.UpdatedAt,
			&a.Assigned,
			&a.UserID,
			&a.UserName,
			&a.UserEmail,
			&a.AppointmentDate,
			&a.AppointmentTime,
		)
		if err != nil {
			log.Println("Scan error:", err)
			continue
		}
		
		// Handle nullable house_alpha
		if houseAlpha != "" {
			a.HouseAlpha = &houseAlpha
		}
		
		addresses = append(addresses, a)
	}

	// Get users associated with this admin
	currentAdminID := r.Context().Value("user_id").(int)
	userRows, err := models.DB.Query(`
		SELECT u.user_id, u.first_name || ' ' || u.last_name AS name
		FROM users u
		JOIN admin_volunteers av ON u.user_id = av.volunteer_id
		WHERE av.admin_id = $1 AND av.is_active = true
	`, currentAdminID)
	if err != nil {
		log.Println("Failed to fetch users:", err)
		http.Error(w, "Database error", http.StatusInternalServerError)
		return
	}
	defer userRows.Close()

	type UserOption struct {
		ID   int
		Name string
	}
	var users []UserOption
	for userRows.Next() {
		var u UserOption
		if err := userRows.Scan(&u.ID, &u.Name); err != nil {
			log.Println("User scan error:", err)
			continue
		}
		users = append(users, u)
	}

	// Pagination info
	startRecord := offset + 1
	endRecord := offset + len(addresses)
	if totalRecords == 0 {
		startRecord = 0
	}

	pageNumbers := generatePageNumbers(page, totalPages)
	pagination := PaginationInfo{
		CurrentPage:  page,
		TotalPages:   totalPages,
		TotalRecords: totalRecords,
		PageSize:     pageSize,
		HasPrevious:  page > 1,
		HasNext:      page < totalPages,
		StartRecord:  startRecord,
		EndRecord:    endRecord,
		PreviousPage: page - 1,
		NextPage:     page + 1,
		FirstPage:    1,
		LastPage:     totalPages,
		PageNumbers:  pageNumbers,
	}

	utils.Render(w, "address/address.html", map[string]interface{}{
		"Title":           "Addresses",
		"IsAuthenticated": true,
		"ShowAdminNav":    true,
		"ActiveSection":   "address",
		"Addresses":       addresses,
		"Users":           users,
		"UserName": 	username,
		"Role":            "admin",
		"Pagination":      pagination,
	})
}

func generatePageNumbers(currentPage, totalPages int) []PageNumber {
	var pageNumbers []PageNumber

	// Generate page numbers to show (max 7 pages)
	start := currentPage - 3
	end := currentPage + 3

	if start < 1 {
		end += 1 - start
		start = 1
	}
	if end > totalPages {
		start -= end - totalPages
		end = totalPages
	}
	if start < 1 {
		start = 1
	}

	for i := start; i <= end; i++ {
		pageNumbers = append(pageNumbers, PageNumber{
			Number:    i,
			IsCurrent: i == currentPage,
		})
	}

	return pageNumbers
}

func AssignAddressHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Redirect(w, r, "/addresses", http.StatusSeeOther)
		return
	}

	if err := r.ParseForm(); err != nil {
		http.Error(w, "Invalid form", http.StatusBadRequest)
		return
	}

	userIDStr := r.FormValue("user_id")
	addressIDStr := r.FormValue("address_id")
	appointmentDate := r.FormValue("appointment_date")
	startTime := r.FormValue("time")

	if userIDStr == "" || addressIDStr == "" {
		http.Error(w, "User ID and Address ID are required", http.StatusBadRequest)
		return
	}

	if appointmentDate == "" || startTime == "" {
		http.Error(w, "Appointment date and start time are required", http.StatusBadRequest)
		return
	}

	userID, err := strconv.Atoi(userIDStr)
	if err != nil {
		http.Error(w, "Invalid user ID", http.StatusBadRequest)
		return
	}

	addressID, err := strconv.Atoi(addressIDStr)
	if err != nil {
		http.Error(w, "Invalid address ID", http.StatusBadRequest)
		return
	}

	// Parse and validate the appointment date
	parsedDate, err := time.Parse("2006-01-02", appointmentDate)
	if err != nil {
		http.Error(w, "Invalid appointment date format", http.StatusBadRequest)
		return
	}

	// Validate that the appointment date is not in the past
	today := time.Now().Truncate(24 * time.Hour)
	if parsedDate.Before(today) {
		http.Error(w, "Appointment date cannot be in the past", http.StatusBadRequest)
		return
	}

	// Parse and validate the start time
	_, err = time.Parse("15:04", startTime)
	if err != nil {
		http.Error(w, "Invalid start time format", http.StatusBadRequest)
		return
	}

	// Verify the user exists and is associated with the current admin
	currentAdminID := r.Context().Value("user_id").(int)
	var userExists int
	err = models.DB.QueryRow(`
		SELECT COUNT(*) FROM admin_volunteers av
		JOIN users u ON av.volunteer_id = u.user_id
		WHERE av.admin_id = $1 AND u.user_id = $2 AND av.is_active = true
	`, currentAdminID, userID).Scan(&userExists)
	if err != nil {
		log.Println("User verification error:", err)
		http.Error(w, "Database error", http.StatusInternalServerError)
		return
	}
	if userExists == 0 {
		http.Error(w, "Invalid user selection", http.StatusBadRequest)
		return
	}

	// Check if this address is already assigned to any user
	var exists int
	err = models.DB.QueryRow(`
		SELECT COUNT(*) FROM appointment 
		WHERE address_id = $1
	`, addressID).Scan(&exists)
	if err != nil {
		log.Println("Assignment check error:", err)
		http.Error(w, "Database error", http.StatusInternalServerError)
		return
	}
	if exists > 0 {
		http.Error(w, "This address is already assigned to another user", http.StatusBadRequest)
		return
	}

	// Check if the user already has an appointment at the same date and time
	var timeConflict int
	err = models.DB.QueryRow(`
		SELECT COUNT(*) FROM appointment 
		WHERE user_id = $1 AND appointment_date = $2 AND appointment_time = $3
	`, userID, appointmentDate, startTime).Scan(&timeConflict)
	if err != nil {
		log.Println("Time conflict check error:", err)
		http.Error(w, "Database error", http.StatusInternalServerError)
		return
	}
	if timeConflict > 0 {
		http.Error(w, "User already has an appointment at this date and time", http.StatusBadRequest)
		return
	}

	// Assign the address - create appointment with specific date and time
	_, err = models.DB.Exec(`
		INSERT INTO appointment (user_id, address_id, appointment_date, appointment_time, created_at, updated_at)
		VALUES ($1, $2, $3, $4, NOW(), NOW())
	`, userID, addressID, appointmentDate, startTime)
	if err != nil {
		log.Println("Assignment error:", err)
		http.Error(w, "Failed to assign address", http.StatusInternalServerError)
		return
	}

	// Redirect back to addresses page with success
	http.Redirect(w, r, "/addresses?success=assigned", http.StatusSeeOther)
}


func RemoveAssignedAddressHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Redirect(w, r, "/addresses", http.StatusSeeOther)
		return
	}

	if err := r.ParseForm(); err != nil {
		http.Error(w, "Invalid form", http.StatusBadRequest)
		return
	}

	userIDStr := r.FormValue("user_id")
	addressIDStr := r.FormValue("address_id")

	if userIDStr == "" || addressIDStr == "" {
		http.Error(w, "User ID and Address ID are required", http.StatusBadRequest)
		return
	}

	userID, err := strconv.Atoi(userIDStr)
	if err != nil {
		http.Error(w, "Invalid user ID", http.StatusBadRequest)
		return
	}

	addressID, err := strconv.Atoi(addressIDStr)
	if err != nil {
		http.Error(w, "Invalid address ID", http.StatusBadRequest)
		return
	}

	// Verify the user is managed by current admin
	currentAdminID := r.Context().Value("user_id").(int)
	var userExists int
	err = models.DB.QueryRow(`
		SELECT COUNT(*) 
		FROM admin_volunteers av
		JOIN appointment ap ON av.volunteer_id = ap.user_id
		WHERE av.admin_id = $1 AND ap.user_id = $2 AND ap.address_id = $3
	`, currentAdminID, userID, addressID).Scan(&userExists)
	if err != nil {
		log.Println("Verification error:", err)
		http.Error(w, "Database error", http.StatusInternalServerError)
		return
	}
	if userExists == 0 {
		http.Error(w, "Unauthorized removal", http.StatusForbidden)
		return
	}

	// Remove volunteer assignment
	_, err = models.DB.Exec(`DELETE FROM appointment WHERE user_id = $1 AND address_id = $2`, userID, addressID)
	if err != nil {
		log.Println("Remove assignment error:", err)
		http.Error(w, "Failed to remove assignment", http.StatusInternalServerError)
		return
	}

	http.Redirect(w, r, "/addresses?success=removed", http.StatusSeeOther)
}
