package importer

import (
	"archive/zip"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	"gitea.mrixs.me/minecraft-platform/backend/internal/models"
)

// CurseForgeImporter реализует импорт для сборок CurseForge.
type CurseForgeImporter struct {
	StoragePath    string
	APIKey         string
	HTTPClient     *http.Client
	HTTPClientLong *http.Client
}

// NewCurseForgeImporter создает новый экземпляр импортера.
func NewCurseForgeImporter(storagePath, apiKey string) *CurseForgeImporter {
	return &CurseForgeImporter{
		StoragePath:    storagePath,
		APIKey:         apiKey,
		HTTPClient:     &http.Client{Timeout: 30 * time.Second},
		HTTPClientLong: &http.Client{Timeout: 10 * time.Minute},
	}
}

// downloadAndProcessFile скачивает файл по URL и передает его в общий обработчик.
func (i *CurseForgeImporter) downloadAndProcessFile(url string) (hash string, size int64, err error) {
	req, err := http.NewRequestWithContext(context.Background(), "GET", url, nil)
	if err != nil {
		return "", 0, err
	}

	resp, err := i.HTTPClientLong.Do(req)
	if err != nil {
		return "", 0, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", 0, fmt.Errorf("bad status downloading file: %s", resp.Status)
	}

	baseImporter := SimpleZipImporter{StoragePath: i.StoragePath}
	return baseImporter.processFile(resp.Body)
}

// getFileInfo получает полную информацию о файле, включая URL для скачивания.
func (i *CurseForgeImporter) getFileInfo(projectID, fileID int) (*CurseForgeFile, error) {
	apiURL := fmt.Sprintf("https://api.curseforge.com/v1/mods/%d/files/%d", projectID, fileID)
	req, err := http.NewRequestWithContext(context.Background(), "GET", apiURL, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("x-api-key", i.APIKey)

	resp, err := i.HTTPClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("bad status getting file info for fileID %d: %s", fileID, resp.Status)
	}

	var fileInfo CurseForgeFile
	if err := json.NewDecoder(resp.Body).Decode(&fileInfo); err != nil {
		return nil, fmt.Errorf("failed to decode file info response: %w", err)
	}

	return &fileInfo, nil
}

// FindModpackBySlug ищет ID проекта по его "слагу" (части URL).
func (i *CurseForgeImporter) FindModpackBySlug(slug string) (int, error) {
	apiURL := fmt.Sprintf("https://api.curseforge.com/v1/mods/search?gameId=432&slug=%s", url.QueryEscape(slug))
	req, err := http.NewRequestWithContext(context.Background(), "GET", apiURL, nil)
	if err != nil {
		return 0, err
	}
	req.Header.Set("x-api-key", i.APIKey)

	resp, err := i.HTTPClient.Do(req)
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()

	var searchResp CurseForgeSearchResponse
	if err := json.NewDecoder(resp.Body).Decode(&searchResp); err != nil {
		return 0, err
	}

	if len(searchResp.Data) == 0 {
		return 0, fmt.Errorf("modpack with slug '%s' not found", slug)
	}

	return searchResp.Data[0].ID, nil
}

// getLatestModpackFileURL находит URL для скачивания последнего файла проекта.
func (i *CurseForgeImporter) getLatestModpackFileURL(projectID int) (string, error) {
	files, err := i.GetModpackFiles(projectID)
	if err != nil {
		return "", err
	}

	if len(files) == 0 {
		return "", fmt.Errorf("no files found for projectID %d", projectID)
	}

	latestFile := files[0]
	return latestFile.DownloadURL, nil
}

// GetModpackFiles возвращает список файлов (версий) для проекта.
func (i *CurseForgeImporter) GetModpackFiles(projectID int) ([]CurseForgeFileData, error) {
	apiURL := fmt.Sprintf("https://api.curseforge.com/v1/mods/%d/files", projectID)
	req, err := http.NewRequestWithContext(context.Background(), "GET", apiURL, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("x-api-key", i.APIKey)

	resp, err := i.HTTPClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var filesResp CurseForgeFilesResponse
	if err := json.NewDecoder(resp.Body).Decode(&filesResp); err != nil {
		return nil, err
	}

	return filesResp.Data, nil
}

// DownloadModpackFromURL скачивает zip-архив модпака по URL страницы CurseForge.
func (i *CurseForgeImporter) DownloadModpackFromURL(pageURL string) (string, error) {
	parsedURL, err := url.Parse(pageURL)
	if err != nil {
		return "", fmt.Errorf("invalid url: %w", err)
	}
	slug := path.Base(parsedURL.Path)
	log.Printf("Importer: Extracted slug '%s' from URL", slug)

	projectID, err := i.FindModpackBySlug(slug)
	if err != nil {
		return "", err
	}
	log.Printf("Importer: Found projectID %d for slug '%s'", projectID, slug)

	downloadURL, err := i.getLatestModpackFileURL(projectID)
	if err != nil {
		return "", err
	}
	log.Printf("Importer: Found download URL: %s", downloadURL)

	tempFile, err := os.CreateTemp("", "modpack-*.zip")
	if err != nil {
		return "", err
	}
	defer tempFile.Close()

	req, err := http.NewRequestWithContext(context.Background(), "GET", downloadURL, nil)
	if err != nil {
		return "", err
	}

	resp, err := i.HTTPClientLong.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if _, err := io.Copy(tempFile, resp.Body); err != nil {
		return "", err
	}

	log.Printf("Importer: Successfully downloaded modpack to %s", tempFile.Name())
	return tempFile.Name(), nil
}

// Import реализует основной метод для CurseForge.
func (i *CurseForgeImporter) Import(zipPath string) ([]models.ModpackFile, error) {
	r, err := zip.OpenReader(zipPath)
	if err != nil {
		return nil, err
	}
	defer r.Close()

	var manifestFile *zip.File
	for _, f := range r.File {
		if f.Name == "manifest.json" {
			manifestFile = f
			break
		}
	}

	if manifestFile == nil {
		return nil, fmt.Errorf("manifest.json not found in archive")
	}

	mfReader, err := manifestFile.Open()
	if err != nil {
		return nil, err
	}
	defer mfReader.Close()

	var manifest CurseForgeManifest
	if err := json.NewDecoder(mfReader).Decode(&manifest); err != nil {
		return nil, fmt.Errorf("failed to parse manifest.json: %w", err)
	}

	var files []models.ModpackFile

	for _, modFile := range manifest.Files {
		fileInfo, err := i.getFileInfo(modFile.ProjectID, modFile.FileID)
		if err != nil {
			log.Printf("Importer: WARN - Could not get info for fileID %d, skipping. Error: %v", modFile.FileID, err)
			continue
		}

		downloadURL := fileInfo.Data.DownloadURL
		fileName := fileInfo.Data.FileName

		// ИСПРАВЛЕНИЕ: Проверяем, что URL не пустой
		if downloadURL == "" {
			log.Printf("Importer: WARN - Empty download URL for file '%s' (fileID %d), skipping.", fileName, modFile.FileID)
			continue // Пропускаем этот файл
		}

		hash, size, err := i.downloadAndProcessFile(downloadURL)
		if err != nil {
			// Вместо того чтобы падать, просто логируем ошибку и пропускаем файл
			log.Printf("Importer: WARN - Failed to process downloaded file '%s' (fileID %d), skipping. Error: %v", fileName, modFile.FileID, err)
			continue
		}

		relativePath := filepath.Join("mods", fileName)

		files = append(files, models.ModpackFile{
			RelativePath: relativePath,
			FileHash:     hash,
			FileSize:     size,
			DownloadURL:  downloadURL,
		})
	}

	baseImporter := SimpleZipImporter{StoragePath: i.StoragePath}
	for _, f := range r.File {
		if strings.HasPrefix(f.Name, manifest.Overrides+"/") && !f.FileInfo().IsDir() {
			fileReader, err := f.Open()
			if err != nil {
				return nil, err
			}

			hash, size, err := baseImporter.processFile(fileReader)
			if err != nil {
				fileReader.Close()
				return nil, err
			}
			fileReader.Close()

			relativePath := strings.TrimPrefix(f.Name, manifest.Overrides+"/")
			files = append(files, models.ModpackFile{
				RelativePath: relativePath,
				FileHash:     hash,
				FileSize:     size,
			})
		}
	}

	return files, nil
}
