package handlers

import (
	"database/sql"
	"encoding/json"
	"html/template"
	"net/http"
	"regexp"
	"strings"
	"time"

	ory "github.com/ory/client-go"

	"decor-by-hannahs/internal/db"
)

func getServiceIDFromOption(option string) int64 {
	serviceMap := map[string]int64{
		"Small Bundle":  1,
		"Medium Bundle": 2,
		"Large Bundle":  3,
		"Balloons Only": 4,
	}
	if id, ok := serviceMap[option]; ok {
		return id
	}
	return 1
}

func validateAustralianAddress(address string) (bool, string) {
	if len(strings.TrimSpace(address)) < 10 {
		return false, "Address is too short. Please provide a complete address."
	}

	australianStates := []string{"NSW", "VIC", "QLD", "SA", "WA", "TAS", "NT", "ACT"}
	hasState := false
	upperAddress := strings.ToUpper(address)
	for _, state := range australianStates {
		if strings.Contains(upperAddress, state) {
			hasState = true
			break
		}
	}

	postcodePattern := regexp.MustCompile(`\b\d{4}\b`)
	hasPostcode := postcodePattern.MatchString(address)

	if !hasState && !hasPostcode {
		return false, "Please include an Australian state (NSW, VIC, QLD, etc.) and postcode in your address."
	}

	if !hasState {
		return false, "Please include an Australian state (NSW, VIC, QLD, SA, WA, TAS, NT, or ACT) in your address."
	}

	if !hasPostcode {
		return false, "Please include a 4-digit postcode in your address."
	}

	return true, ""
}

func BookingPageHandler(q *db.Queries, tmpl *template.Template, oryClient *ory.APIClient) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		type data struct {
			Title         string
			ActivePage    string
			Authenticated bool
			OryLoginURL   string
		}
		if err := tmpl.ExecuteTemplate(w, "booking.tmpl", data{
			Title:         "Book a Service",
			ActivePage:    "booking",
			Authenticated: isAuthenticated(r, oryClient),
			OryLoginURL:   "/login",
		}); err != nil {
			http.Error(w, "Failed to render page", http.StatusInternalServerError)
			return
		}
	}
}

func BookingAPIHandler(q *db.Queries, tmpl *template.Template, oryClient *ory.APIClient) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			http.Error(w, "Method is not allowed", http.StatusMethodNotAllowed)
			return
		}

		cookies := r.Header.Get("Cookie")
		session, _, err := oryClient.FrontendAPI.ToSession(r.Context()).Cookie(cookies).Execute()
		
		if err != nil || session == nil || session.Active == nil || !*session.Active {
			http.Error(w, "Unauthorized", http.StatusUnauthorized)
			return
		}

		email := getEmailFromSession(session)
		if email == "" {
			http.Error(w, "No email in session", http.StatusBadRequest)
			return
		}

		oryID := sql.NullString{String: session.Identity.Id, Valid: true}
		user, err := q.GetUserByOryID(r.Context(), oryID)
		if err != nil {
			user, err = q.CreateUser(r.Context(), db.CreateUserParams{
				Email:         email,
				OryIdentityID: oryID,
			})
			if err != nil {
				http.Error(w, "Failed to get or create user", http.StatusInternalServerError)
				return
			}
		}

		var in struct {
			ServiceOption string `json:"service_option"`
			EventType     string `json:"event_type"`
			EventDate     string `json:"event_date"`
			Address       string `json:"address"`
			Notes         string `json:"notes"`
		}
		if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
			http.Error(w, "Bad request", http.StatusBadRequest)
			return
		}

		if valid, msg := validateAustralianAddress(in.Address); !valid {
			http.Error(w, msg, http.StatusBadRequest)
			return
		}

		eventTime, err := time.Parse(time.RFC3339, in.EventDate)
		if err != nil {
			http.Error(w, "Invalid date", http.StatusBadRequest)
			return
		}

		userID := sql.NullInt64{Int64: user.ID, Valid: true}
		serviceID := getServiceIDFromOption(in.ServiceOption)

		booking, err := q.CreateBooking(r.Context(), db.CreateBookingParams{
			UserID:        userID,
			ServiceID:     sql.NullInt64{Int64: serviceID, Valid: true},
			EventDate:     eventTime,
			Address:       sqlNullString(in.Address),
			Notes:         sqlNullString(in.Notes),
			Status:        sql.NullString{String: "pending", Valid: true},
			ServiceOption: sqlNullString(in.ServiceOption),
			EventType:     sqlNullString(in.EventType),
		})
		if err != nil {
			http.Error(w, "Failed to create booking", http.StatusInternalServerError)
			return
		}
		writeJSON(w, booking)
	}
}
