package handlers

import (
	"encoding/json"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/jackc/pgx/v5/pgxpool"
)

// PaymentHandlers handles payment compliance endpoints
type PaymentHandlers struct {
	pool     *pgxpool.Pool
	controls *PaymentControlLibrary
}

// PaymentControlLibrary holds the control catalog
type PaymentControlLibrary struct {
	Domains  []PaymentDomain  `json:"domains"`
	Controls []PaymentControl `json:"controls"`
}

type PaymentDomain struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	Description string `json:"description"`
}

type PaymentControl struct {
	ControlID  string   `json:"control_id"`
	Domain     string   `json:"domain"`
	Title      string   `json:"title"`
	Objective  string   `json:"objective"`
	CheckTarget string  `json:"check_target"`
	Evidence   []string `json:"evidence"`
	Automation string   `json:"automation"`
}

type PaymentAssessment struct {
	ID              uuid.UUID       `json:"id"`
	TenantID        uuid.UUID       `json:"tenant_id"`
	ProjectName     string          `json:"project_name"`
	TenderReference string          `json:"tender_reference,omitempty"`
	CustomerName    string          `json:"customer_name,omitempty"`
	Description     string          `json:"description,omitempty"`
	SystemType      string          `json:"system_type,omitempty"`
	PaymentMethods  json.RawMessage `json:"payment_methods,omitempty"`
	Protocols       json.RawMessage `json:"protocols,omitempty"`
	TotalControls   int             `json:"total_controls"`
	ControlsPassed  int             `json:"controls_passed"`
	ControlsFailed  int             `json:"controls_failed"`
	ControlsPartial int             `json:"controls_partial"`
	ControlsNA      int             `json:"controls_not_applicable"`
	ControlsUnchecked int           `json:"controls_not_checked"`
	ComplianceScore float64         `json:"compliance_score"`
	Status          string          `json:"status"`
	ControlResults  json.RawMessage `json:"control_results,omitempty"`
	CreatedAt       time.Time       `json:"created_at"`
	UpdatedAt       time.Time       `json:"updated_at"`
	CreatedBy       string          `json:"created_by,omitempty"`
}

// NewPaymentHandlers creates payment handlers with loaded control library
func NewPaymentHandlers(pool *pgxpool.Pool) *PaymentHandlers {
	lib := loadControlLibrary()
	return &PaymentHandlers{pool: pool, controls: lib}
}

func loadControlLibrary() *PaymentControlLibrary {
	// Try to load from policies directory
	paths := []string{
		"policies/payment_controls_v1.json",
		"/app/policies/payment_controls_v1.json",
	}
	for _, p := range paths {
		data, err := os.ReadFile(p)
		if err != nil {
			// Try relative to executable
			execDir, _ := os.Executable()
			altPath := filepath.Join(filepath.Dir(execDir), p)
			data, err = os.ReadFile(altPath)
			if err != nil {
				continue
			}
		}
		var lib PaymentControlLibrary
		if err := json.Unmarshal(data, &lib); err == nil {
			return &lib
		}
	}
	return &PaymentControlLibrary{}
}

// GetControlLibrary returns the loaded control library (for tender matching)
func (h *PaymentHandlers) GetControlLibrary() *PaymentControlLibrary {
	return h.controls
}

// ListControls returns the control library
func (h *PaymentHandlers) ListControls(c *gin.Context) {
	domain := c.Query("domain")
	automation := c.Query("automation")

	controls := h.controls.Controls
	if domain != "" {
		var filtered []PaymentControl
		for _, ctrl := range controls {
			if ctrl.Domain == domain {
				filtered = append(filtered, ctrl)
			}
		}
		controls = filtered
	}
	if automation != "" {
		var filtered []PaymentControl
		for _, ctrl := range controls {
			if ctrl.Automation == automation {
				filtered = append(filtered, ctrl)
			}
		}
		controls = filtered
	}

	c.JSON(http.StatusOK, gin.H{
		"controls": controls,
		"domains":  h.controls.Domains,
		"total":    len(controls),
	})
}

// CreateAssessment creates a new payment compliance assessment
func (h *PaymentHandlers) CreateAssessment(c *gin.Context) {
	var req PaymentAssessment
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	tenantID, _ := uuid.Parse(c.GetHeader("X-Tenant-ID"))
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	req.ID = uuid.New()
	req.TenantID = tenantID
	req.Status = "draft"
	req.TotalControls = len(h.controls.Controls)
	req.ControlsUnchecked = req.TotalControls
	req.CreatedAt = time.Now()
	req.UpdatedAt = time.Now()

	_, err := h.pool.Exec(c.Request.Context(), `
		INSERT INTO payment_compliance_assessments (
			id, tenant_id, project_name, tender_reference, customer_name, description,
			system_type, payment_methods, protocols,
			total_controls, controls_not_checked, status, created_by
		) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)`,
		req.ID, req.TenantID, req.ProjectName, req.TenderReference, req.CustomerName, req.Description,
		req.SystemType, req.PaymentMethods, req.Protocols,
		req.TotalControls, req.ControlsUnchecked, req.Status, req.CreatedBy,
	)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, req)
}

// ListAssessments lists all payment assessments for a tenant
func (h *PaymentHandlers) ListAssessments(c *gin.Context) {
	tenantID, _ := uuid.Parse(c.GetHeader("X-Tenant-ID"))
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	rows, err := h.pool.Query(c.Request.Context(), `
		SELECT id, tenant_id, project_name, tender_reference, customer_name,
			system_type, total_controls, controls_passed, controls_failed,
			controls_partial, controls_not_applicable, controls_not_checked,
			compliance_score, status, created_at, updated_at
		FROM payment_compliance_assessments
		WHERE tenant_id = $1
		ORDER BY created_at DESC`, tenantID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	defer rows.Close()

	var assessments []PaymentAssessment
	for rows.Next() {
		var a PaymentAssessment
		rows.Scan(&a.ID, &a.TenantID, &a.ProjectName, &a.TenderReference, &a.CustomerName,
			&a.SystemType, &a.TotalControls, &a.ControlsPassed, &a.ControlsFailed,
			&a.ControlsPartial, &a.ControlsNA, &a.ControlsUnchecked,
			&a.ComplianceScore, &a.Status, &a.CreatedAt, &a.UpdatedAt)
		assessments = append(assessments, a)
	}
	if assessments == nil {
		assessments = []PaymentAssessment{}
	}

	c.JSON(http.StatusOK, gin.H{"assessments": assessments, "total": len(assessments)})
}

// GetAssessment returns a single assessment with control results
func (h *PaymentHandlers) GetAssessment(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
		return
	}

	var a PaymentAssessment
	err = h.pool.QueryRow(c.Request.Context(), `
		SELECT id, tenant_id, project_name, tender_reference, customer_name, description,
			system_type, payment_methods, protocols,
			total_controls, controls_passed, controls_failed, controls_partial,
			controls_not_applicable, controls_not_checked, compliance_score,
			status, control_results, created_at, updated_at, created_by
		FROM payment_compliance_assessments WHERE id = $1`, id).Scan(
		&a.ID, &a.TenantID, &a.ProjectName, &a.TenderReference, &a.CustomerName, &a.Description,
		&a.SystemType, &a.PaymentMethods, &a.Protocols,
		&a.TotalControls, &a.ControlsPassed, &a.ControlsFailed, &a.ControlsPartial,
		&a.ControlsNA, &a.ControlsUnchecked, &a.ComplianceScore,
		&a.Status, &a.ControlResults, &a.CreatedAt, &a.UpdatedAt, &a.CreatedBy)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "assessment not found"})
		return
	}

	c.JSON(http.StatusOK, a)
}

// UpdateControlVerdict updates the verdict for a single control
func (h *PaymentHandlers) UpdateControlVerdict(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
		return
	}

	var body struct {
		ControlID string `json:"control_id"`
		Verdict   string `json:"verdict"` // passed, failed, partial, na, unchecked
		Evidence  string `json:"evidence,omitempty"`
		Notes     string `json:"notes,omitempty"`
	}
	if err := c.ShouldBindJSON(&body); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Update the control_results JSONB and recalculate scores
	_, err = h.pool.Exec(c.Request.Context(), `
		WITH updated AS (
			SELECT id,
				COALESCE(control_results, '[]'::jsonb) AS existing_results
			FROM payment_compliance_assessments WHERE id = $1
		)
		UPDATE payment_compliance_assessments SET
			control_results = (
				SELECT jsonb_agg(
					CASE WHEN elem->>'control_id' = $2 THEN
						jsonb_build_object('control_id', $2, 'verdict', $3, 'evidence', $4, 'notes', $5)
					ELSE elem END
				) FROM updated, jsonb_array_elements(
					CASE WHEN existing_results @> jsonb_build_array(jsonb_build_object('control_id', $2))
					THEN existing_results
					ELSE existing_results || jsonb_build_array(jsonb_build_object('control_id', $2, 'verdict', $3, 'evidence', $4, 'notes', $5))
					END
				) AS elem
			),
			updated_at = NOW()
		WHERE id = $1`,
		id, body.ControlID, body.Verdict, body.Evidence, body.Notes)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"status": "updated", "control_id": body.ControlID, "verdict": body.Verdict})
}
