package handlers

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

	"github.com/breakpilot/consent-service/internal/middleware"
	"github.com/breakpilot/consent-service/internal/models"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

// ========================================
// ADMIN ENDPOINTS - Version Approval Workflow (DSB)
// ========================================

// AdminSubmitForReview submits a version for DSB review
func (h *Handler) AdminSubmitForReview(c *gin.Context) {
	versionID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid version ID"})
		return
	}

	userID, _ := middleware.GetUserID(c)
	ctx := context.Background()
	ipAddress := middleware.GetClientIP(c)
	userAgent := middleware.GetUserAgent(c)

	// Check current status
	var status string
	err = h.db.Pool.QueryRow(ctx, `SELECT status FROM document_versions WHERE id = $1`, versionID).Scan(&status)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Version not found"})
		return
	}

	if status != "draft" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Only draft versions can be submitted for review"})
		return
	}

	// Update status to review
	_, err = h.db.Pool.Exec(ctx, `
		UPDATE document_versions
		SET status = 'review', updated_at = NOW()
		WHERE id = $1
	`, versionID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to submit for review"})
		return
	}

	// Log approval action
	_, err = h.db.Pool.Exec(ctx, `
		INSERT INTO version_approvals (version_id, approver_id, action, comment)
		VALUES ($1, $2, 'submitted', 'Submitted for DSB review')
	`, versionID, userID)

	h.logAudit(ctx, &userID, "version_submitted_review", "document_version", &versionID, nil, ipAddress, userAgent)

	c.JSON(http.StatusOK, gin.H{"message": "Version submitted for review"})
}

// AdminApproveVersion approves a version with scheduled publish date (DSB only)
func (h *Handler) AdminApproveVersion(c *gin.Context) {
	versionID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid version ID"})
		return
	}

	// Check if user is DSB or Admin (for dev purposes)
	if !middleware.IsDSB(c) && !middleware.IsAdmin(c) {
		c.JSON(http.StatusForbidden, gin.H{"error": "Only Data Protection Officers can approve versions"})
		return
	}

	var req struct {
		Comment            string  `json:"comment"`
		ScheduledPublishAt *string `json:"scheduled_publish_at"` // ISO 8601: "2026-01-01T00:00:00Z"
	}
	c.ShouldBindJSON(&req)

	// Validate scheduled publish date
	var scheduledAt *time.Time
	if req.ScheduledPublishAt != nil && *req.ScheduledPublishAt != "" {
		parsed, err := time.Parse(time.RFC3339, *req.ScheduledPublishAt)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid scheduled_publish_at format. Use ISO 8601 (e.g., 2026-01-01T00:00:00Z)"})
			return
		}
		if parsed.Before(time.Now()) {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Scheduled publish date must be in the future"})
			return
		}
		scheduledAt = &parsed
	}

	userID, _ := middleware.GetUserID(c)
	ctx := context.Background()
	ipAddress := middleware.GetClientIP(c)
	userAgent := middleware.GetUserAgent(c)

	// Check current status
	var status string
	var createdBy *uuid.UUID
	err = h.db.Pool.QueryRow(ctx, `SELECT status, created_by FROM document_versions WHERE id = $1`, versionID).Scan(&status, &createdBy)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Version not found"})
		return
	}

	if status != "review" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Only versions in review status can be approved"})
		return
	}

	// Four-eyes principle: DSB cannot approve their own version
	// Exception: Admins can approve their own versions for development/testing purposes
	role, _ := c.Get("role")
	roleStr, _ := role.(string)
	if createdBy != nil && *createdBy == userID && roleStr != "admin" {
		c.JSON(http.StatusForbidden, gin.H{"error": "You cannot approve your own version (four-eyes principle)"})
		return
	}

	// Determine new status: 'scheduled' if date set, otherwise 'approved'
	newStatus := "approved"
	if scheduledAt != nil {
		newStatus = "scheduled"
	}

	// Update status to approved/scheduled
	_, err = h.db.Pool.Exec(ctx, `
		UPDATE document_versions
		SET status = $2, approved_by = $3, approved_at = NOW(), scheduled_publish_at = $4, updated_at = NOW()
		WHERE id = $1
	`, versionID, newStatus, userID, scheduledAt)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to approve version"})
		return
	}

	// Log approval action
	comment := req.Comment
	if comment == "" {
		if scheduledAt != nil {
			comment = "Approved by DSB, scheduled for " + scheduledAt.Format("02.01.2006 15:04")
		} else {
			comment = "Approved by DSB"
		}
	}
	_, err = h.db.Pool.Exec(ctx, `
		INSERT INTO version_approvals (version_id, approver_id, action, comment)
		VALUES ($1, $2, 'approved', $3)
	`, versionID, userID, comment)

	h.logAudit(ctx, &userID, "version_approved", "document_version", &versionID, &comment, ipAddress, userAgent)

	response := gin.H{"message": "Version approved", "status": newStatus}
	if scheduledAt != nil {
		response["scheduled_publish_at"] = scheduledAt.Format(time.RFC3339)
	}
	c.JSON(http.StatusOK, response)
}

// AdminRejectVersion rejects a version (DSB only)
func (h *Handler) AdminRejectVersion(c *gin.Context) {
	versionID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid version ID"})
		return
	}

	// Check if user is DSB
	if !middleware.IsDSB(c) {
		c.JSON(http.StatusForbidden, gin.H{"error": "Only Data Protection Officers can reject versions"})
		return
	}

	var req struct {
		Comment string `json:"comment" binding:"required"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Comment is required when rejecting"})
		return
	}

	userID, _ := middleware.GetUserID(c)
	ctx := context.Background()
	ipAddress := middleware.GetClientIP(c)
	userAgent := middleware.GetUserAgent(c)

	// Check current status
	var status string
	err = h.db.Pool.QueryRow(ctx, `SELECT status FROM document_versions WHERE id = $1`, versionID).Scan(&status)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Version not found"})
		return
	}

	if status != "review" && status != "approved" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Only versions in review or approved status can be rejected"})
		return
	}

	// Update status back to draft
	_, err = h.db.Pool.Exec(ctx, `
		UPDATE document_versions
		SET status = 'draft', approved_by = NULL, updated_at = NOW()
		WHERE id = $1
	`, versionID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to reject version"})
		return
	}

	// Log rejection
	_, err = h.db.Pool.Exec(ctx, `
		INSERT INTO version_approvals (version_id, approver_id, action, comment)
		VALUES ($1, $2, 'rejected', $3)
	`, versionID, userID, req.Comment)

	h.logAudit(ctx, &userID, "version_rejected", "document_version", &versionID, &req.Comment, ipAddress, userAgent)

	c.JSON(http.StatusOK, gin.H{"message": "Version rejected and returned to draft"})
}

// AdminCompareVersions returns two versions for side-by-side comparison
func (h *Handler) AdminCompareVersions(c *gin.Context) {
	versionID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid version ID"})
		return
	}

	ctx := context.Background()

	// Get the current version and its document
	var currentVersion models.DocumentVersion
	var documentID uuid.UUID
	err = h.db.Pool.QueryRow(ctx, `
		SELECT id, document_id, version, language, title, content, summary, status, created_at, updated_at
		FROM document_versions
		WHERE id = $1
	`, versionID).Scan(&currentVersion.ID, &documentID, &currentVersion.Version, &currentVersion.Language,
		&currentVersion.Title, &currentVersion.Content, &currentVersion.Summary, &currentVersion.Status,
		&currentVersion.CreatedAt, &currentVersion.UpdatedAt)

	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Version not found"})
		return
	}

	// Get the currently published version (if any)
	var publishedVersion *models.DocumentVersion
	var pv models.DocumentVersion
	err = h.db.Pool.QueryRow(ctx, `
		SELECT id, document_id, version, language, title, content, summary, status, published_at, created_at, updated_at
		FROM document_versions
		WHERE document_id = $1 AND language = $2 AND status = 'published'
		ORDER BY published_at DESC
		LIMIT 1
	`, documentID, currentVersion.Language).Scan(&pv.ID, &pv.DocumentID, &pv.Version, &pv.Language,
		&pv.Title, &pv.Content, &pv.Summary, &pv.Status, &pv.PublishedAt, &pv.CreatedAt, &pv.UpdatedAt)

	if err == nil && pv.ID != currentVersion.ID {
		publishedVersion = &pv
	}

	// Get approval history
	rows, err := h.db.Pool.Query(ctx, `
		SELECT va.action, va.comment, va.created_at, u.email
		FROM version_approvals va
		LEFT JOIN users u ON va.approver_id = u.id
		WHERE va.version_id = $1
		ORDER BY va.created_at DESC
	`, versionID)

	var approvalHistory []map[string]interface{}
	if err == nil {
		defer rows.Close()
		for rows.Next() {
			var action, email string
			var comment *string
			var createdAt time.Time
			if err := rows.Scan(&action, &comment, &createdAt, &email); err == nil {
				approvalHistory = append(approvalHistory, map[string]interface{}{
					"action":     action,
					"comment":    comment,
					"created_at": createdAt,
					"approver":   email,
				})
			}
		}
	}

	c.JSON(http.StatusOK, gin.H{
		"current_version":   currentVersion,
		"published_version": publishedVersion,
		"approval_history":  approvalHistory,
	})
}

// AdminGetApprovalHistory returns the approval history for a version
func (h *Handler) AdminGetApprovalHistory(c *gin.Context) {
	versionID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid version ID"})
		return
	}

	ctx := context.Background()

	rows, err := h.db.Pool.Query(ctx, `
		SELECT va.id, va.action, va.comment, va.created_at, u.email, u.name
		FROM version_approvals va
		LEFT JOIN users u ON va.approver_id = u.id
		WHERE va.version_id = $1
		ORDER BY va.created_at DESC
	`, versionID)

	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch approval history"})
		return
	}
	defer rows.Close()

	var history []map[string]interface{}
	for rows.Next() {
		var id uuid.UUID
		var action string
		var comment *string
		var createdAt time.Time
		var email, name *string

		if err := rows.Scan(&id, &action, &comment, &createdAt, &email, &name); err != nil {
			continue
		}

		history = append(history, map[string]interface{}{
			"id":         id,
			"action":     action,
			"comment":    comment,
			"created_at": createdAt,
			"approver":   email,
			"name":       name,
		})
	}

	c.JSON(http.StatusOK, gin.H{"approval_history": history})
}

// ========================================
// SCHEDULED PUBLISHING
// ========================================

// ProcessScheduledPublishing publishes all versions that are due
// This should be called by a cron job or scheduler
func (h *Handler) ProcessScheduledPublishing(c *gin.Context) {
	ctx := context.Background()

	// Find all scheduled versions that are due
	rows, err := h.db.Pool.Query(ctx, `
		SELECT id, document_id, version
		FROM document_versions
		WHERE status = 'scheduled'
		  AND scheduled_publish_at IS NOT NULL
		  AND scheduled_publish_at <= NOW()
	`)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch scheduled versions"})
		return
	}
	defer rows.Close()

	var published []string
	for rows.Next() {
		var versionID, docID uuid.UUID
		var version string
		if err := rows.Scan(&versionID, &docID, &version); err != nil {
			continue
		}

		// Publish this version
		_, err := h.db.Pool.Exec(ctx, `
			UPDATE document_versions
			SET status = 'published', published_at = NOW(), updated_at = NOW()
			WHERE id = $1
		`, versionID)

		if err == nil {
			// Archive previous published versions for this document
			h.db.Pool.Exec(ctx, `
				UPDATE document_versions
				SET status = 'archived', updated_at = NOW()
				WHERE document_id = $1 AND id != $2 AND status = 'published'
			`, docID, versionID)

			// Log the publishing
			details := fmt.Sprintf("Version %s automatically published by scheduler", version)
			h.logAudit(ctx, nil, "version_scheduled_published", "document_version", &versionID, &details, "", "scheduler")

			published = append(published, version)
		}
	}

	c.JSON(http.StatusOK, gin.H{
		"message":           "Scheduled publishing processed",
		"published_count":   len(published),
		"published_versions": published,
	})
}

// GetScheduledVersions returns all versions scheduled for publishing
func (h *Handler) GetScheduledVersions(c *gin.Context) {
	ctx := context.Background()

	rows, err := h.db.Pool.Query(ctx, `
		SELECT dv.id, dv.document_id, dv.version, dv.title, dv.scheduled_publish_at, ld.name as document_name
		FROM document_versions dv
		JOIN legal_documents ld ON ld.id = dv.document_id
		WHERE dv.status = 'scheduled'
		  AND dv.scheduled_publish_at IS NOT NULL
		ORDER BY dv.scheduled_publish_at ASC
	`)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch scheduled versions"})
		return
	}
	defer rows.Close()

	type ScheduledVersion struct {
		ID                 uuid.UUID  `json:"id"`
		DocumentID         uuid.UUID  `json:"document_id"`
		Version            string     `json:"version"`
		Title              string     `json:"title"`
		ScheduledPublishAt *time.Time `json:"scheduled_publish_at"`
		DocumentName       string     `json:"document_name"`
	}

	var versions []ScheduledVersion
	for rows.Next() {
		var v ScheduledVersion
		if err := rows.Scan(&v.ID, &v.DocumentID, &v.Version, &v.Title, &v.ScheduledPublishAt, &v.DocumentName); err != nil {
			continue
		}
		versions = append(versions, v)
	}

	c.JSON(http.StatusOK, gin.H{"scheduled_versions": versions})
}
