package api

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"log/slog"
	"net/http"
	"net/url"
	"os"
	"path"

	"gitea.mrixs.me/minecraft-platform/backend/internal/core"
	"gitea.mrixs.me/minecraft-platform/backend/internal/core/importer"
	"gitea.mrixs.me/minecraft-platform/backend/internal/database"
	"gitea.mrixs.me/minecraft-platform/backend/internal/models"
	"gitea.mrixs.me/minecraft-platform/backend/internal/ws"
)

type ModpackHandler struct {
	ModpackRepo    *database.ModpackRepository
	JobRepo        *database.JobRepository
	JanitorService *core.FileJanitorService
	Hub            *ws.Hub
}

// ImportJobParams содержит параметры для фоновой задачи импорта
type ImportJobParams struct {
	ImporterType string
	ImportMethod string
	SourceURL    string
	TempZipPath  string
	Name         string
	DisplayName  string
	MCVersion    string
}

// ImportModpack обрабатывает запрос на импорт и запускает асинхронную задачу
func (h *ModpackHandler) ImportModpack(w http.ResponseWriter, r *http.Request) {
	if err := r.ParseMultipartForm(512 << 20); err != nil {
		http.Error(w, "File too large", http.StatusBadRequest)
		return
	}

	params := ImportJobParams{
		ImporterType: r.FormValue("importerType"),
		ImportMethod: r.FormValue("importMethod"),
		SourceURL:    r.FormValue("sourceUrl"),
		Name:         r.FormValue("name"),
		DisplayName:  r.FormValue("displayName"),
		MCVersion:    r.FormValue("mcVersion"),
	}

	// Валидация
	if params.Name == "" || params.DisplayName == "" || params.MCVersion == "" {
		http.Error(w, "Missing required fields", http.StatusBadRequest)
		return
	}

	// Обработка загрузки файла
	if params.ImportMethod == "file" {
		file, _, err := r.FormFile("file")
		if err != nil {
			http.Error(w, "Invalid file upload", http.StatusBadRequest)
			return
		}
		defer file.Close()

		tempFile, err := os.CreateTemp("", "modpack-*.zip")
		if err != nil {
			http.Error(w, "Could not create temp file", http.StatusInternalServerError)
			return
		}
		// Не удаляем файл здесь, так как он нужен worker-у. Удалим в worker-е.
		// defer os.Remove(tempFile.Name())

		if _, err := io.Copy(tempFile, file); err != nil {
			tempFile.Close()
			os.Remove(tempFile.Name())
			http.Error(w, "Could not save temp file", http.StatusInternalServerError)
			return
		}
		tempFile.Close()
		params.TempZipPath = tempFile.Name()
	}

	// Создаем задачу в БД
	jobID, err := h.JobRepo.CreateJob(r.Context())
	if err != nil {
		if params.ImportMethod == "file" {
			os.Remove(params.TempZipPath)
		}
		http.Error(w, fmt.Sprintf("Failed to create job: %v", err), http.StatusInternalServerError)
		return
	}

	// Запускаем worker
	go h.processImportJob(jobID, params)

	// Отправляем ответ
	w.WriteHeader(http.StatusAccepted)
	json.NewEncoder(w).Encode(map[string]interface{}{
		"job_id":  jobID,
		"message": "Import job started",
	})
}

// processImportJob выполняет импорт в фоне
func (h *ModpackHandler) processImportJob(jobID int, params ImportJobParams) {
	ctx := context.Background()
	h.updateJobStatus(ctx, jobID, models.JobStatusDownloading, 10, "")

	// Очистка временного файла по завершении
	if params.TempZipPath != "" {
		defer os.Remove(params.TempZipPath)
	}

	storagePath := os.Getenv("MODPACKS_STORAGE_PATH")
	var imp importer.ModpackImporter

	// Настройка импортера
	switch params.ImporterType {
	case "simple":
		imp = &importer.SimpleZipImporter{StoragePath: storagePath}
	case "curseforge":
		apiKey := os.Getenv("CURSEFORGE_API_KEY")
		if apiKey == "" {
			h.updateJobStatus(ctx, jobID, models.JobStatusFailed, 0, "CurseForge API key missing")
			return
		}
		imp = importer.NewCurseForgeImporter(storagePath, apiKey)
	case "modrinth":
		imp = importer.NewModrinthImporter(storagePath)
	default:
		h.updateJobStatus(ctx, jobID, models.JobStatusFailed, 0, "Invalid importer type")
		return
	}

	// Загрузка по URL если нужно
	if params.ImportMethod == "url" {
		h.updateJobStatus(ctx, jobID, models.JobStatusDownloading, 20, "Downloading from URL...")

		// Логика скачивания зависит от типа импортера, пока поддерживаем CurseForge
		// TODO: Сделать интерфейс DownloadableImporter
		if cfImporter, ok := imp.(*importer.CurseForgeImporter); ok {
			zipPath, err := cfImporter.DownloadModpackFromURL(params.SourceURL)
			if err != nil {
				h.updateJobStatus(ctx, jobID, models.JobStatusFailed, 0, fmt.Sprintf("Download failed: %v", err))
				return
			}
			params.TempZipPath = zipPath
			defer os.Remove(zipPath) // Удаляем скачанный файл после обработки
		} else {
			// Для других типов пока не поддерживаем URL download внутри импортера
			h.updateJobStatus(ctx, jobID, models.JobStatusFailed, 0, "URL import not supported for this type")
			return
		}
	}

	h.updateJobStatus(ctx, jobID, models.JobStatusProcessing, 40, "Processing modpack files...")

	// Импорт файлов
	files, err := imp.Import(params.TempZipPath)
	if err != nil {
		h.updateJobStatus(ctx, jobID, models.JobStatusFailed, 0, fmt.Sprintf("Import failed: %v", err))
		return
	}

	h.updateJobStatus(ctx, jobID, models.JobStatusProcessing, 80, "Saving to database...")

	// Сохранение в БД
	modpack := &models.Modpack{
		Name:             params.Name,
		DisplayName:      params.DisplayName,
		MinecraftVersion: params.MCVersion,
	}

	err = h.ModpackRepo.CreateModpackTx(ctx, modpack, files)
	if err != nil {
		h.updateJobStatus(ctx, jobID, models.JobStatusFailed, 0, fmt.Sprintf("Database save failed: %v", err))
		return
	}

	h.updateJobStatus(ctx, jobID, models.JobStatusCompleted, 100, "Success")

	// Запуск Janitor-а
	go h.JanitorService.CleanOrphanedFiles(context.Background())
}

// updateJobStatus обновляет статус в БД и отправляет уведомление через WebSocket
func (h *ModpackHandler) updateJobStatus(ctx context.Context, jobID int, status models.ImportJobStatus, progress int, errMsg string) {
	// Обновляем БД
	if err := h.JobRepo.UpdateJobStatus(ctx, jobID, status, progress, errMsg); err != nil {
		slog.Error("Failed to update job status in DB", "jobID", jobID, "error", err)
	}

	// Отправляем в WebSocket
	update := map[string]interface{}{
		"job_id":        jobID,
		"status":        status,
		"progress":      progress,
		"error_message": errMsg,
	}
	msg, _ := json.Marshal(update)
	h.Hub.BroadcastMessage(msg)
}

// ModpackVersionResponse представляет версию модпака для ответа API
type ModpackVersionResponse struct {
	FileID       int      `json:"file_id"`
	DisplayName  string   `json:"display_name"`
	FileName     string   `json:"file_name"`
	FileDate     string   `json:"file_date"`
	GameVersions []string `json:"game_versions"`
}

// GetModpackVersions возвращает список доступных версий для модпака по URL
func (h *ModpackHandler) GetModpackVersions(w http.ResponseWriter, r *http.Request) {
	pageURL := r.URL.Query().Get("url")
	if pageURL == "" {
		http.Error(w, "Missing 'url' query parameter", http.StatusBadRequest)
		return
	}

	apiKey := os.Getenv("CURSEFORGE_API_KEY")
	if apiKey == "" {
		http.Error(w, "CurseForge API key not configured", http.StatusInternalServerError)
		return
	}

	cfImporter := importer.NewCurseForgeImporter("", apiKey)

	// Извлекаем slug из URL
	parsedURL, err := parseModpackURL(pageURL)
	if err != nil {
		http.Error(w, fmt.Sprintf("Invalid URL: %v", err), http.StatusBadRequest)
		return
	}

	// Ищем проект по slug
	projectID, err := cfImporter.FindModpackBySlug(parsedURL)
	if err != nil {
		http.Error(w, fmt.Sprintf("Modpack not found: %v", err), http.StatusNotFound)
		return
	}

	// Получаем список файлов (версий)
	files, err := cfImporter.GetModpackFiles(projectID)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to get versions: %v", err), http.StatusInternalServerError)
		return
	}

	// Преобразуем в ответ
	var versions []ModpackVersionResponse
	for _, f := range files {
		versions = append(versions, ModpackVersionResponse{
			FileID:       f.ID,
			DisplayName:  f.DisplayName,
			FileName:     f.FileName,
			FileDate:     f.FileDate,
			GameVersions: f.GameVersions,
		})
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(versions)
}

// parseModpackURL извлекает slug из URL страницы CurseForge
func parseModpackURL(rawURL string) (string, error) {
	parsed, err := url.Parse(rawURL)
	if err != nil {
		return "", err
	}
	// URL вида https://www.curseforge.com/minecraft/modpacks/all-the-mods-9
	// path.Base вернет "all-the-mods-9"
	slug := path.Base(parsed.Path)
	if slug == "" || slug == "." || slug == "/" {
		return "", fmt.Errorf("could not extract modpack slug from URL")
	}
	return slug, nil
}

// GetModpacks возвращает список всех модпаков для админки
func (h *ModpackHandler) GetModpacks(w http.ResponseWriter, r *http.Request) {
	modpacks, err := h.ModpackRepo.GetAllModpacks(r.Context())
	if err != nil {
		slog.Error("Failed to get modpacks", "error", err)
		http.Error(w, "Failed to get modpacks", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(modpacks)
}

// UpdateJobParams содержит параметры для фоновой задачи обновления
type UpdateJobParams struct {
	ImporterType string
	ImportMethod string
	SourceURL    string
	TempZipPath  string
	ModpackID    int
	ModpackName  string
	MCVersion    string
}

// UpdateModpack обрабатывает запрос на обновление модпака
func (h *ModpackHandler) UpdateModpack(w http.ResponseWriter, r *http.Request) {
	if err := r.ParseMultipartForm(512 << 20); err != nil {
		http.Error(w, "File too large", http.StatusBadRequest)
		return
	}

	modpackName := r.FormValue("modpackName")
	if modpackName == "" {
		http.Error(w, "Missing modpack name", http.StatusBadRequest)
		return
	}

	// Получаем модпак по имени
	modpack, err := h.ModpackRepo.GetModpackByName(r.Context(), modpackName)
	if err != nil {
		http.Error(w, "Modpack not found", http.StatusNotFound)
		return
	}

	params := UpdateJobParams{
		ImporterType: r.FormValue("importerType"),
		ImportMethod: r.FormValue("importMethod"),
		SourceURL:    r.FormValue("sourceUrl"),
		ModpackID:    modpack.ID,
		ModpackName:  modpackName,
		MCVersion:    r.FormValue("mcVersion"),
	}

	if params.MCVersion == "" {
		params.MCVersion = modpack.MinecraftVersion
	}

	// Обработка загрузки файла
	if params.ImportMethod == "file" {
		file, _, err := r.FormFile("file")
		if err != nil {
			http.Error(w, "Invalid file upload", http.StatusBadRequest)
			return
		}
		defer file.Close()

		tempFile, err := os.CreateTemp("", "modpack-update-*.zip")
		if err != nil {
			http.Error(w, "Could not create temp file", http.StatusInternalServerError)
			return
		}

		if _, err := io.Copy(tempFile, file); err != nil {
			tempFile.Close()
			os.Remove(tempFile.Name())
			http.Error(w, "Could not save temp file", http.StatusInternalServerError)
			return
		}
		tempFile.Close()
		params.TempZipPath = tempFile.Name()
	}

	// Создаем задачу в БД
	jobID, err := h.JobRepo.CreateJob(r.Context())
	if err != nil {
		if params.ImportMethod == "file" {
			os.Remove(params.TempZipPath)
		}
		http.Error(w, fmt.Sprintf("Failed to create job: %v", err), http.StatusInternalServerError)
		return
	}

	// Запускаем worker
	go h.processUpdateJob(jobID, params)

	// Отправляем ответ
	w.WriteHeader(http.StatusAccepted)
	json.NewEncoder(w).Encode(map[string]interface{}{
		"job_id":  jobID,
		"message": "Update job started",
	})
}

// processUpdateJob выполняет обновление модпака в фоне
func (h *ModpackHandler) processUpdateJob(jobID int, params UpdateJobParams) {
	ctx := context.Background()
	h.updateJobStatus(ctx, jobID, models.JobStatusDownloading, 10, "")

	// Очистка временного файла по завершении
	if params.TempZipPath != "" {
		defer os.Remove(params.TempZipPath)
	}

	storagePath := os.Getenv("MODPACKS_STORAGE_PATH")
	var imp importer.ModpackImporter

	// Настройка импортера
	switch params.ImporterType {
	case "simple":
		imp = &importer.SimpleZipImporter{StoragePath: storagePath}
	case "curseforge":
		apiKey := os.Getenv("CURSEFORGE_API_KEY")
		if apiKey == "" {
			h.updateJobStatus(ctx, jobID, models.JobStatusFailed, 0, "CurseForge API key missing")
			return
		}
		imp = importer.NewCurseForgeImporter(storagePath, apiKey)
	case "modrinth":
		imp = importer.NewModrinthImporter(storagePath)
	default:
		h.updateJobStatus(ctx, jobID, models.JobStatusFailed, 0, "Invalid importer type")
		return
	}

	// Загрузка по URL если нужно
	if params.ImportMethod == "url" {
		h.updateJobStatus(ctx, jobID, models.JobStatusDownloading, 20, "Downloading from URL...")

		if cfImporter, ok := imp.(*importer.CurseForgeImporter); ok {
			zipPath, err := cfImporter.DownloadModpackFromURL(params.SourceURL)
			if err != nil {
				h.updateJobStatus(ctx, jobID, models.JobStatusFailed, 0, fmt.Sprintf("Download failed: %v", err))
				return
			}
			params.TempZipPath = zipPath
			defer os.Remove(zipPath)
		} else {
			h.updateJobStatus(ctx, jobID, models.JobStatusFailed, 0, "URL import not supported for this type")
			return
		}
	}

	h.updateJobStatus(ctx, jobID, models.JobStatusProcessing, 40, "Processing modpack files...")

	// Импорт файлов
	files, err := imp.Import(params.TempZipPath)
	if err != nil {
		h.updateJobStatus(ctx, jobID, models.JobStatusFailed, 0, fmt.Sprintf("Import failed: %v", err))
		return
	}

	h.updateJobStatus(ctx, jobID, models.JobStatusProcessing, 80, "Updating database...")

	// Обновление в БД
	err = h.ModpackRepo.UpdateModpackTx(ctx, params.ModpackID, params.MCVersion, files)
	if err != nil {
		h.updateJobStatus(ctx, jobID, models.JobStatusFailed, 0, fmt.Sprintf("Database update failed: %v", err))
		return
	}

	h.updateJobStatus(ctx, jobID, models.JobStatusCompleted, 100, "Success")

	// Запуск Janitor-а
	go h.JanitorService.CleanOrphanedFiles(context.Background())
}
