package handlers

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

	"github.com/google/uuid"

	"mgit.msbls.de/m/KanzlAI-mGMT/internal/auth"
	"mgit.msbls.de/m/KanzlAI-mGMT/internal/models"
	"mgit.msbls.de/m/KanzlAI-mGMT/internal/services"
)

type AppointmentHandler struct {
	svc *services.AppointmentService
}

func NewAppointmentHandler(svc *services.AppointmentService) *AppointmentHandler {
	return &AppointmentHandler{svc: svc}
}

// Get handles GET /api/appointments/{id}
func (h *AppointmentHandler) Get(w http.ResponseWriter, r *http.Request) {
	tenantID, ok := auth.TenantFromContext(r.Context())
	if !ok {
		writeError(w, http.StatusUnauthorized, "missing tenant")
		return
	}

	id, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid appointment id")
		return
	}

	appt, err := h.svc.GetByID(r.Context(), tenantID, id)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			writeError(w, http.StatusNotFound, "appointment not found")
			return
		}
		writeError(w, http.StatusInternalServerError, "failed to fetch appointment")
		return
	}

	writeJSON(w, http.StatusOK, appt)
}

func (h *AppointmentHandler) List(w http.ResponseWriter, r *http.Request) {
	tenantID, ok := auth.TenantFromContext(r.Context())
	if !ok {
		writeError(w, http.StatusUnauthorized, "missing tenant")
		return
	}

	filter := services.AppointmentFilter{}

	if v := r.URL.Query().Get("case_id"); v != "" {
		id, err := uuid.Parse(v)
		if err != nil {
			writeError(w, http.StatusBadRequest, "invalid case_id")
			return
		}
		filter.CaseID = &id
	}
	if v := r.URL.Query().Get("type"); v != "" {
		filter.Type = &v
	}
	if v := r.URL.Query().Get("start_from"); v != "" {
		t, err := time.Parse(time.RFC3339, v)
		if err != nil {
			writeError(w, http.StatusBadRequest, "invalid start_from (use RFC3339)")
			return
		}
		filter.StartFrom = &t
	}
	if v := r.URL.Query().Get("start_to"); v != "" {
		t, err := time.Parse(time.RFC3339, v)
		if err != nil {
			writeError(w, http.StatusBadRequest, "invalid start_to (use RFC3339)")
			return
		}
		filter.StartTo = &t
	}

	appointments, err := h.svc.List(r.Context(), tenantID, filter)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "failed to list appointments")
		return
	}

	writeJSON(w, http.StatusOK, appointments)
}

type createAppointmentRequest struct {
	CaseID          *uuid.UUID `json:"case_id"`
	Title           string     `json:"title"`
	Description     *string    `json:"description"`
	StartAt         time.Time  `json:"start_at"`
	EndAt           *time.Time `json:"end_at"`
	Location        *string    `json:"location"`
	AppointmentType *string    `json:"appointment_type"`
}

func (h *AppointmentHandler) Create(w http.ResponseWriter, r *http.Request) {
	tenantID, ok := auth.TenantFromContext(r.Context())
	if !ok {
		writeError(w, http.StatusUnauthorized, "missing tenant")
		return
	}

	var req createAppointmentRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "invalid request body")
		return
	}
	if req.Title == "" {
		writeError(w, http.StatusBadRequest, "title is required")
		return
	}
	if msg := validateStringLength("title", req.Title, maxTitleLen); msg != "" {
		writeError(w, http.StatusBadRequest, msg)
		return
	}
	if req.StartAt.IsZero() {
		writeError(w, http.StatusBadRequest, "start_at is required")
		return
	}

	appt := &models.Appointment{
		TenantID:        tenantID,
		CaseID:          req.CaseID,
		Title:           req.Title,
		Description:     req.Description,
		StartAt:         req.StartAt,
		EndAt:           req.EndAt,
		Location:        req.Location,
		AppointmentType: req.AppointmentType,
	}

	if err := h.svc.Create(r.Context(), appt); err != nil {
		writeError(w, http.StatusInternalServerError, "failed to create appointment")
		return
	}

	writeJSON(w, http.StatusCreated, appt)
}

type updateAppointmentRequest struct {
	CaseID          *uuid.UUID `json:"case_id"`
	Title           string     `json:"title"`
	Description     *string    `json:"description"`
	StartAt         time.Time  `json:"start_at"`
	EndAt           *time.Time `json:"end_at"`
	Location        *string    `json:"location"`
	AppointmentType *string    `json:"appointment_type"`
}

func (h *AppointmentHandler) Update(w http.ResponseWriter, r *http.Request) {
	tenantID, ok := auth.TenantFromContext(r.Context())
	if !ok {
		writeError(w, http.StatusUnauthorized, "missing tenant")
		return
	}

	id, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid appointment id")
		return
	}

	// Fetch existing to verify ownership
	existing, err := h.svc.GetByID(r.Context(), tenantID, id)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			writeError(w, http.StatusNotFound, "appointment not found")
			return
		}
		writeError(w, http.StatusInternalServerError, "failed to fetch appointment")
		return
	}

	var req updateAppointmentRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "invalid request body")
		return
	}
	if req.Title == "" {
		writeError(w, http.StatusBadRequest, "title is required")
		return
	}
	if msg := validateStringLength("title", req.Title, maxTitleLen); msg != "" {
		writeError(w, http.StatusBadRequest, msg)
		return
	}
	if req.StartAt.IsZero() {
		writeError(w, http.StatusBadRequest, "start_at is required")
		return
	}

	existing.CaseID = req.CaseID
	existing.Title = req.Title
	existing.Description = req.Description
	existing.StartAt = req.StartAt
	existing.EndAt = req.EndAt
	existing.Location = req.Location
	existing.AppointmentType = req.AppointmentType

	if err := h.svc.Update(r.Context(), existing); err != nil {
		writeError(w, http.StatusInternalServerError, "failed to update appointment")
		return
	}

	writeJSON(w, http.StatusOK, existing)
}

func (h *AppointmentHandler) Delete(w http.ResponseWriter, r *http.Request) {
	tenantID, ok := auth.TenantFromContext(r.Context())
	if !ok {
		writeError(w, http.StatusUnauthorized, "missing tenant")
		return
	}

	id, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid appointment id")
		return
	}

	if err := h.svc.Delete(r.Context(), tenantID, id); err != nil {
		writeError(w, http.StatusNotFound, "appointment not found")
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

