package handlers

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/go-chi/chi/v5"
	"github.com/orchard9/rdev/internal/domain"
	"github.com/orchard9/rdev/pkg/api"
)

// DomainAliasRequest is the request body for POST /projects/{id}/domains.
type DomainAliasRequest struct {
	Domain     string `json:"domain"`                // The domain to add (e.g., "my-app.threesix.ai" or "custom.example.com")
	Type       string `json:"type,omitempty"`        // "A" or "CNAME" (default: "A")
	Proxied    bool   `json:"proxied,omitempty"`     // Cloudflare proxy (default: false)
	DomainType string `json:"domain_type,omitempty"` // "primary_custom" or "alias" (default: "alias")
}

// DomainResponse is the response for domain operations.
type DomainResponse struct {
	ID         int64  `json:"id"`
	Domain     string `json:"domain"`
	Type       string `json:"type"`        // DomainType: primary_auto, primary_custom, alias
	RecordType string `json:"record_type"` // DNS record type: A, CNAME
	Verified   bool   `json:"verified"`
	CreatedAt  string `json:"created_at"`
}

// ListDomains returns all domains associated with a project.
// GET /projects/{id}/domains
func (h *InfrastructureHandler) ListDomains(w http.ResponseWriter, r *http.Request) {
	projectID := chi.URLParam(r, "id")
	ctx, cancel := context.WithTimeout(r.Context(), TimeoutLookup)
	defer cancel()

	if err := validateProjectID(projectID); err != nil {
		api.WriteBadRequest(w, r, err.Error())
		return
	}

	if h.domainService == nil {
		api.WriteInternalError(w, r, "domain service not configured")
		return
	}

	domains, err := h.domainService.ListDomains(ctx, projectID)
	if err != nil {
		api.WriteInternalError(w, r, fmt.Sprintf("failed to list domains: %v", err))
		return
	}

	// Convert to response format
	response := make([]DomainResponse, 0, len(domains))
	for _, d := range domains {
		response = append(response, DomainResponse{
			ID:         d.ID,
			Domain:     d.Domain,
			Type:       string(d.Type),
			RecordType: d.DNSRecordType,
			Verified:   d.Verified,
			CreatedAt:  d.CreatedAt.Format(time.RFC3339),
		})
	}

	api.WriteSuccess(w, r, map[string]any{
		"project_id": projectID,
		"domains":    response,
		"total":      len(response),
	})
}

// AddDomainAlias adds a domain to a project.
// POST /projects/{id}/domains
func (h *InfrastructureHandler) AddDomainAlias(w http.ResponseWriter, r *http.Request) {
	projectID := chi.URLParam(r, "id")
	ctx, cancel := context.WithTimeout(r.Context(), TimeoutStandard)
	defer cancel()

	if err := validateProjectID(projectID); err != nil {
		api.WriteBadRequest(w, r, err.Error())
		return
	}

	if h.domainService == nil {
		api.WriteInternalError(w, r, "domain service not configured")
		return
	}

	var req DomainAliasRequest
	if err := api.DecodeJSON(r, &req); err != nil {
		api.WriteBadRequest(w, r, "invalid request body")
		return
	}

	if req.Domain == "" {
		api.WriteBadRequest(w, r, "domain is required")
		return
	}

	// Default record type is A
	recordType := strings.ToUpper(req.Type)
	if recordType == "" {
		recordType = "A"
	}
	if recordType != "A" && recordType != "CNAME" {
		api.WriteBadRequest(w, r, "type must be A or CNAME")
		return
	}

	// Determine domain type
	domainType := domain.DomainTypeAlias
	if req.DomainType == "primary_custom" {
		domainType = domain.DomainTypePrimaryCustom
	}

	pd, err := h.domainService.AddDomain(ctx, DomainAddRequest{
		ProjectID:  projectID,
		Domain:     req.Domain,
		Type:       domainType,
		RecordType: recordType,
		Proxied:    req.Proxied,
	})
	if err != nil {
		if errors.Is(err, domain.ErrDuplicateDomain) {
			api.WriteBadRequest(w, r, "domain already exists")
			return
		}
		api.WriteInternalError(w, r, fmt.Sprintf("failed to add domain: %v", err))
		return
	}

	note := "Domain configured"
	if !isSubdomain(req.Domain, h.defaultDomain) && recordType == "A" {
		note = fmt.Sprintf("External domain configured. Point your DNS to %s", h.clusterIP)
	}

	api.WriteCreated(w, r, map[string]any{
		"id":          pd.ID,
		"project":     projectID,
		"domain":      pd.Domain,
		"type":        string(pd.Type),
		"record_type": pd.DNSRecordType,
		"verified":    pd.Verified,
		"status":      "configured",
		"note":        note,
	})
}

// RemoveDomainAlias removes a domain from a project.
// DELETE /projects/{id}/domains/{domain}
func (h *InfrastructureHandler) RemoveDomainAlias(w http.ResponseWriter, r *http.Request) {
	projectID := chi.URLParam(r, "id")
	aliasDomain := chi.URLParam(r, "domain")
	ctx, cancel := context.WithTimeout(r.Context(), TimeoutStandard)
	defer cancel()

	if err := validateProjectID(projectID); err != nil {
		api.WriteBadRequest(w, r, err.Error())
		return
	}

	if aliasDomain == "" {
		api.WriteBadRequest(w, r, "domain path parameter is required")
		return
	}

	if h.domainService == nil {
		api.WriteInternalError(w, r, "domain service not configured")
		return
	}

	err := h.domainService.RemoveDomain(ctx, projectID, aliasDomain)
	if err != nil {
		if errors.Is(err, domain.ErrDomainNotFound) {
			api.WriteNotFound(w, r, fmt.Sprintf("domain not found: %s", aliasDomain))
			return
		}
		// Check for primary domain protection
		if strings.Contains(err.Error(), "auto-generated primary domain") {
			api.WriteBadRequest(w, r, "cannot remove auto-generated primary domain; delete the project instead")
			return
		}
		api.WriteInternalError(w, r, fmt.Sprintf("failed to remove domain: %v", err))
		return
	}

	api.WriteSuccess(w, r, map[string]string{
		"project": projectID,
		"domain":  aliasDomain,
		"status":  "removed",
	})
}
