package handlers

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

type BackupHandler struct {
	backupDir string
}

type BackupInfo struct {
	Filename  string `json:"filename"`
	Size      string `json:"size"`
	Date      string `json:"date"`
	Timestamp string `json:"timestamp"`
}

func NewBackupHandler() *BackupHandler {
	// Usa o caminho montado no container
	backupDir := "/backups"
	
	// Garante que o diretório existe
	if _, err := os.Stat(backupDir); os.IsNotExist(err) {
		os.MkdirAll(backupDir, 0755)
	}
	
	return &BackupHandler{
		backupDir: backupDir,
	}
}

// ListBackups lista todos os backups disponíveis
func (h *BackupHandler) ListBackups(w http.ResponseWriter, r *http.Request) {
	files, err := ioutil.ReadDir(h.backupDir)
	if err != nil {
		http.Error(w, "Error reading backups directory", http.StatusInternalServerError)
		return
	}

	var backups []BackupInfo
	for _, file := range files {
		if strings.HasPrefix(file.Name(), "aggios_backup_") && strings.HasSuffix(file.Name(), ".sql") {
			// Extrai timestamp do nome do arquivo
			timestamp := strings.TrimPrefix(file.Name(), "aggios_backup_")
			timestamp = strings.TrimSuffix(timestamp, ".sql")
			
			// Formata a data
			t, _ := time.Parse("2006-01-02_15-04-05", timestamp)
			dateStr := t.Format("02/01/2006 15:04:05")
			
			// Formata o tamanho
			sizeMB := float64(file.Size()) / 1024
			sizeStr := fmt.Sprintf("%.2f KB", sizeMB)
			
			backups = append(backups, BackupInfo{
				Filename:  file.Name(),
				Size:      sizeStr,
				Date:      dateStr,
				Timestamp: timestamp,
			})
		}
	}
	
	// Ordena por data (mais recente primeiro)
	sort.Slice(backups, func(i, j int) bool {
		return backups[i].Timestamp > backups[j].Timestamp
	})

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"backups": backups,
	})
}

// CreateBackup cria um novo backup do banco de dados
func (h *BackupHandler) CreateBackup(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	timestamp := time.Now().Format("2006-01-02_15-04-05")
	filename := fmt.Sprintf("aggios_backup_%s.sql", timestamp)
	filepath := filepath.Join(h.backupDir, filename)

	// Usa pg_dump diretamente (backend e postgres estão na mesma rede docker)
	dbPassword := os.Getenv("DB_PASSWORD")
	if dbPassword == "" {
		dbPassword = "A9g10s_S3cur3_P@ssw0rd_2025!"
	}
	
	cmd := exec.Command("pg_dump", 
		"-h", "postgres",
		"-U", "aggios", 
		"-d", "aggios_db",
		"--no-password")
	
	// Define a variável de ambiente para a senha
	cmd.Env = append(os.Environ(), fmt.Sprintf("PGPASSWORD=%s", dbPassword))
	
	output, err := cmd.Output()
	if err != nil {
		http.Error(w, fmt.Sprintf("Error creating backup: %v", err), http.StatusInternalServerError)
		return
	}

	// Salva o backup no arquivo
	err = ioutil.WriteFile(filepath, output, 0644)
	if err != nil {
		http.Error(w, fmt.Sprintf("Error saving backup: %v", err), http.StatusInternalServerError)
		return
	}

	// Limpa backups antigos (mantém apenas os últimos 10)
	h.cleanOldBackups()

	fileInfo, _ := os.Stat(filepath)
	sizeMB := float64(fileInfo.Size()) / 1024
	sizeStr := fmt.Sprintf("%.2f KB", sizeMB)

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"message":  "Backup created successfully",
		"filename": filename,
		"size":     sizeStr,
	})
}

// RestoreBackup restaura um backup específico
func (h *BackupHandler) RestoreBackup(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	var req struct {
		Filename string `json:"filename"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request", http.StatusBadRequest)
		return
	}

	if req.Filename == "" {
		http.Error(w, "Filename is required", http.StatusBadRequest)
		return
	}

	// Valida que o arquivo existe e está no diretório correto
	backupPath := filepath.Join(h.backupDir, req.Filename)
	if !strings.HasPrefix(backupPath, h.backupDir) {
		http.Error(w, "Invalid filename", http.StatusBadRequest)
		return
	}

	if _, err := os.Stat(backupPath); os.IsNotExist(err) {
		http.Error(w, "Backup file not found", http.StatusNotFound)
		return
	}

	// Lê o conteúdo do backup
	backupContent, err := ioutil.ReadFile(backupPath)
	if err != nil {
		http.Error(w, fmt.Sprintf("Error reading backup: %v", err), http.StatusInternalServerError)
		return
	}

	// Restaura o backup usando psql diretamente
	dbPassword := os.Getenv("DB_PASSWORD")
	if dbPassword == "" {
		dbPassword = "A9g10s_S3cur3_P@ssw0rd_2025!"
	}
	
	cmd := exec.Command("psql",
		"-h", "postgres",
		"-U", "aggios",
		"-d", "aggios_db",
		"--no-password")
	cmd.Stdin = strings.NewReader(string(backupContent))
	cmd.Env = append(os.Environ(), fmt.Sprintf("PGPASSWORD=%s", dbPassword))
	
	if err := cmd.Run(); err != nil {
		http.Error(w, fmt.Sprintf("Error restoring backup: %v", err), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"message": "Backup restored successfully",
	})
}

// DownloadBackup permite fazer download de um backup
func (h *BackupHandler) DownloadBackup(w http.ResponseWriter, r *http.Request) {
	// Extrai o filename da URL
	parts := strings.Split(r.URL.Path, "/")
	filename := parts[len(parts)-1]

	if filename == "" {
		http.Error(w, "Filename is required", http.StatusBadRequest)
		return
	}

	// Valida que o arquivo existe e está no diretório correto
	backupPath := filepath.Join(h.backupDir, filename)
	if !strings.HasPrefix(backupPath, h.backupDir) {
		http.Error(w, "Invalid filename", http.StatusBadRequest)
		return
	}

	if _, err := os.Stat(backupPath); os.IsNotExist(err) {
		http.Error(w, "Backup file not found", http.StatusNotFound)
		return
	}

	// Lê o arquivo
	data, err := ioutil.ReadFile(backupPath)
	if err != nil {
		http.Error(w, "Error reading file", http.StatusInternalServerError)
		return
	}

	// Define headers para download
	w.Header().Set("Content-Type", "application/sql")
	w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
	w.Write(data)
}

// cleanOldBackups mantém apenas os últimos 10 backups
func (h *BackupHandler) cleanOldBackups() {
	files, err := ioutil.ReadDir(h.backupDir)
	if err != nil {
		return
	}

	var backupFiles []os.FileInfo
	for _, file := range files {
		if strings.HasPrefix(file.Name(), "aggios_backup_") && strings.HasSuffix(file.Name(), ".sql") {
			backupFiles = append(backupFiles, file)
		}
	}

	// Ordena por data de modificação (mais recente primeiro)
	sort.Slice(backupFiles, func(i, j int) bool {
		return backupFiles[i].ModTime().After(backupFiles[j].ModTime())
	})

	// Remove backups antigos (mantém os 10 mais recentes)
	if len(backupFiles) > 10 {
		for _, file := range backupFiles[10:] {
			os.Remove(filepath.Join(h.backupDir, file.Name()))
		}
	}
}
