package handlers

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/juanatsap/cv-site/internal/models"
	"github.com/juanatsap/cv-site/internal/pdf"
	"github.com/juanatsap/cv-site/internal/templates"
)

// CVHandler handles CV-related requests
type CVHandler struct {
	templates    *templates.Manager
	pdfGenerator *pdf.Generator
	serverAddr   string
}

// NewCVHandler creates a new CV handler
func NewCVHandler(tmpl *templates.Manager, serverAddr string) *CVHandler {
	return &CVHandler{
		templates:    tmpl,
		pdfGenerator: pdf.NewGenerator(30 * time.Second),
		serverAddr:   serverAddr,
	}
}

// Home renders the full CV page
func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request) {
	// Get language from query parameter, default to English
	lang := r.URL.Query().Get("lang")
	if lang == "" {
		lang = "en"
	}

	// Validate language
	if lang != "en" && lang != "es" {
		HandleError(w, r, BadRequestError("Unsupported language. Use 'en' or 'es'"))
		return
	}

	// Load CV data
	cv, err := models.LoadCV(lang)
	if err != nil {
		HandleError(w, r, DataLoadError(err, "CV"))
		return
	}

	// Load UI translations
	ui, err := models.LoadUI(lang)
	if err != nil {
		HandleError(w, r, DataLoadError(err, "UI"))
		return
	}

	// Calculate duration for each experience
	for i := range cv.Experience {
		cv.Experience[i].Duration = calculateDuration(
			cv.Experience[i].StartDate,
			cv.Experience[i].EndDate,
			cv.Experience[i].Current,
			lang,
		)
	}

	// Process projects for dynamic dates
	for i := range cv.Projects {
		processProjectDates(&cv.Projects[i], lang)
	}

	// Split skills between left and right sidebars
	skillsLeft, skillsRight := splitSkills(cv.Skills.Technical)

	// Calculate years of experience
	yearsOfExperience := calculateYearsOfExperience()

	// Get current year
	currentYear := time.Now().Year()

	// Prepare template data
	data := map[string]interface{}{
		"CV":                 cv,
		"UI":                 ui,
		"Lang":               lang,
		"SkillsLeft":         skillsLeft,
		"SkillsRight":        skillsRight,
		"YearsOfExperience":  yearsOfExperience,
		"CurrentYear":        currentYear,
	}

	// Render template
	tmpl, err := h.templates.Render("index.html")
	if err != nil {
		HandleError(w, r, TemplateError(err, "index.html"))
		return
	}

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	if err := tmpl.Execute(w, data); err != nil {
		HandleError(w, r, TemplateError(err, "index.html"))
		return
	}
}

// CVContent renders just the CV content for HTMX swaps
func (h *CVHandler) CVContent(w http.ResponseWriter, r *http.Request) {
	// Get language from query parameter
	lang := r.URL.Query().Get("lang")
	if lang == "" {
		lang = "en"
	}

	// Validate language
	if lang != "en" && lang != "es" {
		HandleError(w, r, BadRequestError("Unsupported language. Use 'en' or 'es'"))
		return
	}

	// Load CV data
	cv, err := models.LoadCV(lang)
	if err != nil {
		HandleError(w, r, DataLoadError(err, "CV"))
		return
	}

	// Load UI translations
	ui, err := models.LoadUI(lang)
	if err != nil {
		HandleError(w, r, DataLoadError(err, "UI"))
		return
	}

	// Calculate duration for each experience
	for i := range cv.Experience {
		cv.Experience[i].Duration = calculateDuration(
			cv.Experience[i].StartDate,
			cv.Experience[i].EndDate,
			cv.Experience[i].Current,
			lang,
		)
	}

	// Process projects for dynamic dates
	for i := range cv.Projects {
		processProjectDates(&cv.Projects[i], lang)
	}

	// Split skills between left and right sidebars
	skillsLeft, skillsRight := splitSkills(cv.Skills.Technical)

	// Calculate years of experience
	yearsOfExperience := calculateYearsOfExperience()

	// Get current year
	currentYear := time.Now().Year()

	// Prepare template data
	data := map[string]interface{}{
		"CV":                 cv,
		"UI":                 ui,
		"Lang":               lang,
		"SkillsLeft":         skillsLeft,
		"SkillsRight":        skillsRight,
		"YearsOfExperience":  yearsOfExperience,
		"CurrentYear":        currentYear,
	}

	// Render template
	tmpl, err := h.templates.Render("cv-content.html")
	if err != nil {
		HandleError(w, r, TemplateError(err, "cv-content.html"))
		return
	}

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	if err := tmpl.Execute(w, data); err != nil {
		HandleError(w, r, TemplateError(err, "cv-content.html"))
		return
	}
}

// ExportPDF handles PDF export requests using chromedp
// TEMPORARILY DISABLED - Work in progress
func (h *CVHandler) ExportPDF(w http.ResponseWriter, r *http.Request) {
	// Get language from query parameter
	lang := r.URL.Query().Get("lang")
	if lang == "" {
		lang = "en"
	}

	// Validate language
	if lang != "en" && lang != "es" {
		HandleError(w, r, BadRequestError("Unsupported language. Use 'en' or 'es'"))
		return
	}

	log.Printf("PDF export requested but temporarily disabled (redirecting to print friendly)")

	// Return HTML with message and redirect to print friendly
	message := "PDF Export - Work in Progress"
	body := "The PDF export feature is currently being improved. Please use the <strong>Print Friendly</strong> button instead (Ctrl+P or Cmd+P to save as PDF)."
	if lang == "es" {
		message = "Exportación PDF - En Desarrollo"
		body = "La función de exportación a PDF está siendo mejorada. Por favor, usa el botón <strong>Imprimir Amigable</strong> en su lugar (Ctrl+P o Cmd+P para guardar como PDF)."
	}

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.WriteHeader(http.StatusOK)

	redirectMsg := "Redirecting in 5 seconds..."
	if lang == "es" {
		redirectMsg = "Redirigiendo en 5 segundos..."
	}

	html := fmt.Sprintf(`<!DOCTYPE html>
<html lang="%s">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>%s</title>
    <style>
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            margin: 0;
            background: linear-gradient(135deg, #667eea 0%%, #764ba2 100%%);
        }
        .container {
            background: white;
            padding: 3rem;
            border-radius: 12px;
            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
            max-width: 500px;
            text-align: center;
        }
        h1 {
            color: #333;
            margin: 0 0 1rem 0;
            font-size: 1.8rem;
        }
        p {
            color: #666;
            line-height: 1.6;
            margin: 1rem 0;
        }
        .redirect-info {
            margin-top: 2rem;
            padding: 1rem;
            background: #f0f4ff;
            border-radius: 8px;
            font-size: 0.9rem;
            color: #555;
        }
        .icon {
            font-size: 3rem;
            margin-bottom: 1rem;
        }
    </style>
    <script>
        setTimeout(function() {
            window.location.href = '/?lang=%s';
        }, 5000);
    </script>
</head>
<body>
    <div class="container">
        <div class="icon">🚧</div>
        <h1>%s</h1>
        <p>%s</p>
        <div class="redirect-info">
            %s
        </div>
    </div>
</body>
</html>`, lang, message, lang, message, body, redirectMsg)

	if _, err := w.Write([]byte(html)); err != nil {
		log.Printf("Error writing response: %v", err)
	}
}

// splitSkills splits skill categories between left (page 1) and right (page 2) sidebars
// Each category explicitly specifies which sidebar it belongs to via the "sidebar" field
func splitSkills(skills []models.SkillCategory) (left, right []models.SkillCategory) {
	if len(skills) == 0 {
		return nil, nil
	}

	// Filter by sidebar field
	for _, skill := range skills {
		if skill.Sidebar == "right" {
			right = append(right, skill)
		} else {
			// Default to left if not specified or if set to "left"
			left = append(left, skill)
		}
	}

	return left, right
}

// calculateYearsOfExperience calculates years of experience since April 1, 2005
// This matches the original React implementation that calculated from 01/04/2005
func calculateYearsOfExperience() int {
	// First day at work: April 1, 2005
	firstDay := time.Date(2005, time.April, 1, 9, 0, 0, 0, time.UTC)

	// Current date
	now := time.Now()

	// Calculate the difference in years
	years := now.Year() - firstDay.Year()

	// Adjust if we haven't reached the anniversary this year yet
	if now.Month() < firstDay.Month() ||
	   (now.Month() == firstDay.Month() && now.Day() < firstDay.Day()) {
		years--
	}

	return years
}

// calculateDuration calculates the duration between two dates in years and months
// Date format expected: "YYYY-MM" (e.g., "2021-01")
// Returns a formatted string like "3 years 6 months" or "6 months"
func calculateDuration(startDate, endDate string, current bool, lang string) string {
	// Parse start date
	start, err := time.Parse("2006-01", startDate)
	if err != nil {
		return ""
	}

	// Determine end date
	var end time.Time
	if current {
		end = time.Now()
	} else {
		end, err = time.Parse("2006-01", endDate)
		if err != nil {
			return ""
		}
	}

	// Calculate total months
	totalMonths := (end.Year()-start.Year())*12 + int(end.Month()-start.Month())

	// If end date is before start date, return empty
	if totalMonths < 0 {
		return ""
	}

	years := totalMonths / 12
	months := totalMonths % 12

	// Format the duration string based on language
	var result string
	if lang == "es" {
		if years > 0 && months > 0 {
			yearStr := "años"
			if years == 1 {
				yearStr = "año"
			}
			monthStr := "meses"
			if months == 1 {
				monthStr = "mes"
			}
			result = fmt.Sprintf("(%d %s %d %s)", years, yearStr, months, monthStr)
		} else if years > 0 {
			yearStr := "años"
			if years == 1 {
				yearStr = "año"
			}
			result = fmt.Sprintf("(%d %s)", years, yearStr)
		} else {
			monthStr := "meses"
			if months == 1 {
				monthStr = "mes"
			}
			result = fmt.Sprintf("(%d %s)", months, monthStr)
		}
	} else {
		if years > 0 && months > 0 {
			yearStr := "years"
			if years == 1 {
				yearStr = "year"
			}
			monthStr := "months"
			if months == 1 {
				monthStr = "month"
			}
			result = fmt.Sprintf("(%d %s %d %s)", years, yearStr, months, monthStr)
		} else if years > 0 {
			yearStr := "years"
			if years == 1 {
				yearStr = "year"
			}
			result = fmt.Sprintf("(%d %s)", years, yearStr)
		} else {
			monthStr := "months"
			if months == 1 {
				monthStr = "month"
			}
			result = fmt.Sprintf("(%d %s)", months, monthStr)
		}
	}

	return result
}

// processProjectDates calculates dynamic dates for projects
// If a project has a gitRepoUrl, it fetches the first commit date
// For current projects, it sets the current system date
func processProjectDates(project *models.Project, lang string) {
	now := time.Now()

	// Set dynamic current date for ongoing projects
	if project.Current {
		if lang == "es" {
			project.DynamicDate = "Presente"
		} else {
			project.DynamicDate = "Present"
		}
	}

	// If project has a git repository URL, fetch the first commit date
	if project.GitRepoUrl != "" {
		commitDate := getGitRepoFirstCommitDate(project.GitRepoUrl)
		if commitDate != "" {
			project.ComputedStartDate = commitDate
		}
	}

	// If no computed date and no static date, use current date for current projects
	if project.ComputedStartDate == "" && project.StartDate == "" && project.Current {
		project.ComputedStartDate = now.Format("2006-01")
	}

	// If we have a computed date but no static date, use the computed one
	if project.ComputedStartDate != "" && project.StartDate == "" {
		project.StartDate = project.ComputedStartDate
	}
}

// validateRepoPath validates that a repository path is safe to use
// Security: Prevents path traversal and command injection attacks
// Only allows paths within the project directory
func validateRepoPath(path string) error {
	// Resolve to absolute path to prevent path traversal
	absPath, err := filepath.Abs(path)
	if err != nil {
		return fmt.Errorf("invalid path: %w", err)
	}

	// Get project root directory
	projectRoot, err := filepath.Abs(".")
	if err != nil {
		return fmt.Errorf("cannot determine project root: %w", err)
	}

	// Security check: Only allow paths within project directory
	// This prevents malicious paths like "../../../etc/passwd"
	if !strings.HasPrefix(absPath, projectRoot) {
		return fmt.Errorf("repository path outside project directory: %s", path)
	}

	// Verify path exists and is a directory
	info, err := os.Stat(absPath)
	if err != nil {
		return fmt.Errorf("path does not exist: %w", err)
	}
	if !info.IsDir() {
		return fmt.Errorf("path is not a directory: %s", path)
	}

	return nil
}

// getGitRepoFirstCommitDate fetches the first commit date from a git repository
// Supports local git repository paths
// Security: Validates path and uses timeout to prevent hanging
func getGitRepoFirstCommitDate(repoPath string) string {
	// Security: Validate repository path before executing git command
	if err := validateRepoPath(repoPath); err != nil {
		log.Printf("Security: Rejected git operation for invalid path %s: %v", repoPath, err)
		return ""
	}

	// Security: Add timeout context to prevent hanging
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	// Execute git command with timeout protection
	// Using CommandContext for automatic cancellation on timeout
	cmd := exec.CommandContext(ctx, "git", "-C", repoPath, "log", "--reverse", "--format=%ci", "--date=format:%Y-%m")

	output, err := cmd.Output()
	if err != nil {
		// Log error but don't expose details to prevent information disclosure
		log.Printf("Git command failed for path %s: %v", repoPath, err)
		return ""
	}

	// Parse the output to get the first commit date
	lines := strings.Split(strings.TrimSpace(string(output)), "\n")
	if len(lines) == 0 {
		return ""
	}

	// Extract YYYY-MM from the first commit timestamp
	// Format of output: "2024-06-15 10:30:45 +0200"
	firstLine := lines[0]
	parts := strings.Fields(firstLine)
	if len(parts) > 0 {
		datePart := parts[0] // "2024-06-15"
		dateParts := strings.Split(datePart, "-")
		if len(dateParts) >= 2 {
			return dateParts[0] + "-" + dateParts[1] // "2024-06"
		}
	}

	return ""
}
