package controllers

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"fotbal-club/internal/services"
	"fotbal-club/pkg/logger"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

type GalleryController struct {
	DB *gorm.DB
}

func NewGalleryController(db *gorm.DB) *GalleryController {
	return &GalleryController{DB: db}
}

// ZoneramaAlbum represents a single album with photos
type ZoneramaAlbum struct {
	ID          string          `json:"id"`
	Title       string          `json:"title"`
	URL         string          `json:"url"`
	Date        string          `json:"date"`
	PhotosCount int             `json:"photos_count"`
	ViewsCount  int             `json:"views_count"`
	Photos      []ZoneramaPhoto `json:"photos"`
	FetchedAt   string          `json:"fetched_at,omitempty"`
}

// ZoneramaPhoto represents a single photo
type ZoneramaPhoto struct {
	ID        string `json:"id"`
	PageURL   string `json:"page_url"`
	Image1500 string `json:"image_1500"`
}

// ZoneramaProfile represents the profile with list of albums (metadata only)
type ZoneramaProfile struct {
	InputLink string `json:"input_link"`
	Albums    []struct {
		ID          string `json:"id"`
		Title       string `json:"title"`
		URL         string `json:"url"`
		Date        string `json:"date"`
		PhotosCount int    `json:"photos_count"`
		ViewsCount  int    `json:"views_count"`
	} `json:"albums"`
	FetchedAt string `json:"fetched_at"`
}

// GetGalleryAlbums returns all fetched albums (public)
func (gc *GalleryController) GetGalleryAlbums(c *gin.Context) {
	albumsFile := filepath.Join("cache", "prefetch", "zonerama_albums.json")
	data, err := os.ReadFile(albumsFile)
	if err != nil {
		c.JSON(http.StatusNoContent, gin.H{"albums": []interface{}{}})
		return
	}

	var albums []ZoneramaAlbum
	if err := json.Unmarshal(data, &albums); err != nil {
		logger.Error("Failed to parse albums JSON: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid albums data"})
		return
	}

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

// GetGalleryAlbum returns a single album by ID (public)
func (gc *GalleryController) GetGalleryAlbum(c *gin.Context) {
	albumID := c.Param("id")
	if albumID == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing album ID"})
		return
	}

	albumsFile := filepath.Join("cache", "prefetch", "zonerama_albums.json")
	data, err := os.ReadFile(albumsFile)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Albums not found"})
		return
	}

	var albums []ZoneramaAlbum
	if err := json.Unmarshal(data, &albums); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid albums data"})
		return
	}

	for _, album := range albums {
		if album.ID == albumID {
			c.JSON(http.StatusOK, album)
			return
		}
	}

	c.JSON(http.StatusNotFound, gin.H{"error": "Album not found"})
}

// GetGalleryProfile returns the profile metadata (admin)
func (gc *GalleryController) GetGalleryProfile(c *gin.Context) {
	profileFile := filepath.Join("cache", "prefetch", "zonerama_profile.json")
	data, err := os.ReadFile(profileFile)
	if err != nil {
		c.JSON(http.StatusNoContent, gin.H{})
		return
	}

	var profile ZoneramaProfile
	if err := json.Unmarshal(data, &profile); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid profile data"})
		return
	}

	c.JSON(http.StatusOK, profile)
}

// FetchAlbum fetches a single album and adds it to the albums collection (admin)
func (gc *GalleryController) FetchAlbum(c *gin.Context) {
	var body struct {
		Link       string `json:"link" binding:"required"`
		PhotoLimit int    `json:"photo_limit"`
	}

	if err := c.ShouldBindJSON(&body); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request: " + err.Error()})
		return
	}

	// Validate it's an album URL
	if !strings.Contains(body.Link, "/Album/") {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Link must be a Zonerama album URL (must contain /Album/)"})
		return
	}

	// Set default photo limit
	if body.PhotoLimit == 0 {
		body.PhotoLimit = 50 // Default to 50 photos per album
	}

	// Call external API
	apiURL := fmt.Sprintf("https://zonerama.tdvorak.dev/zonerama-album?link=%s&photo_limit=%d",
		body.Link, body.PhotoLimit)

	logger.Info("Fetching album from Zonerama API: %s", apiURL)

	client := &http.Client{Timeout: 60 * time.Second}
	req, err := http.NewRequest("GET", apiURL, nil)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create request"})
		return
	}
	req.Header.Set("User-Agent", "fotbal-club/1.0")

	resp, err := client.Do(req)
	if err != nil {
		logger.Error("Album fetch failed: %v", err)
		c.JSON(http.StatusBadGateway, gin.H{"error": "Failed to fetch album: " + err.Error()})
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		logger.Error("Album API returned status %d", resp.StatusCode)
		c.JSON(http.StatusBadGateway, gin.H{"error": fmt.Sprintf("Zonerama API returned status %d", resp.StatusCode)})
		return
	}

	// Zonerama API returns {"input_link": "...", "albums": [...]}
	var apiResponse struct {
		InputLink string          `json:"input_link"`
		Albums    []ZoneramaAlbum `json:"albums"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&apiResponse); err != nil {
		logger.Error("Failed to parse album response: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to parse album data"})
		return
	}

	if len(apiResponse.Albums) == 0 {
		logger.Error("No albums returned from API")
		c.JSON(http.StatusNotFound, gin.H{"error": "Album not found in API response"})
		return
	}

	// Get the first album (should be the only one for single album endpoint)
	albumData := apiResponse.Albums[0]

	// Add fetched timestamp
	albumData.FetchedAt = time.Now().Format(time.RFC3339)

	// Load existing albums
	albumsFile := filepath.Join("cache", "prefetch", "zonerama_albums.json")
	var albums []ZoneramaAlbum

	if data, err := os.ReadFile(albumsFile); err == nil {
		_ = json.Unmarshal(data, &albums)
	}

	// Check if album already exists and update it, or add new
	found := false
	for i, a := range albums {
		if a.ID == albumData.ID {
			albums[i] = albumData
			found = true
			logger.Info("Updated existing album: %s", albumData.ID)
			break
		}
	}

	if !found {
		albums = append([]ZoneramaAlbum{albumData}, albums...) // Prepend new album
		logger.Info("Added new album: %s", albumData.ID)
	}

	// Save back to file
	if err := os.MkdirAll(filepath.Dir(albumsFile), 0755); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create cache directory"})
		return
	}

	albumsJSON, err := json.MarshalIndent(albums, "", "  ")
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to serialize albums"})
		return
	}

	if err := os.WriteFile(albumsFile, albumsJSON, 0644); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save albums"})
		return
	}

	logger.Info("Album %s saved successfully with %d photos", albumData.ID, len(albumData.Photos))
	
	// Regenerate flat gallery files for frontend consumption
	if err := services.RegenerateFlatGalleryFiles(); err != nil {
		logger.Error("Failed to regenerate flat gallery files: %v", err)
		// Don't fail the request, just log the error
	}
	
	c.JSON(http.StatusOK, gin.H{
		"message": "Album fetched and saved successfully",
		"album":   albumData,
	})
}

// DeleteAlbum removes an album from the collection (admin)
func (gc *GalleryController) DeleteAlbum(c *gin.Context) {
	albumID := c.Param("id")
	if albumID == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing album ID"})
		return
	}

	albumsFile := filepath.Join("cache", "prefetch", "zonerama_albums.json")
	data, err := os.ReadFile(albumsFile)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Albums not found"})
		return
	}

	var albums []ZoneramaAlbum
	if err := json.Unmarshal(data, &albums); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid albums data"})
		return
	}

	// Find and remove
	newAlbums := []ZoneramaAlbum{}
	found := false
	for _, album := range albums {
		if album.ID != albumID {
			newAlbums = append(newAlbums, album)
		} else {
			found = true
		}
	}

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

	// Save back
	albumsJSON, _ := json.MarshalIndent(newAlbums, "", "  ")
	if err := os.WriteFile(albumsFile, albumsJSON, 0644); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save albums"})
		return
	}

	logger.Info("Deleted album: %s", albumID)
	
	// Regenerate flat gallery files for frontend consumption
	if err := services.RegenerateFlatGalleryFiles(); err != nil {
		logger.Error("Failed to regenerate flat gallery files: %v", err)
		// Don't fail the request, just log the error
	}
	
	c.JSON(http.StatusOK, gin.H{"message": "Album deleted successfully"})
}

// RefreshFromZonerama triggers a refresh of the Zonerama gallery from the configured URL (admin)
func (gc *GalleryController) RefreshFromZonerama(c *gin.Context) {
	// Load settings to get the Zonerama URL
	var settings struct {
		GalleryURL string `json:"gallery_url"`
	}
	
	if err := gc.DB.Table("settings").Select("gallery_url").First(&settings).Error; err != nil {
		logger.Error("Failed to load settings: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load gallery settings"})
		return
	}
	
	zoneramaURL := strings.TrimSpace(settings.GalleryURL)
	if zoneramaURL == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Zonerama URL is not configured in settings"})
		return
	}
	
	// Validate it's a Zonerama URL
	if !strings.Contains(strings.ToLower(zoneramaURL), "zonerama.com") {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Configured gallery URL is not a Zonerama URL"})
		return
	}
	
	logger.Info("Triggering Zonerama refresh from: %s", zoneramaURL)
	
	// Call the refresh service in a goroutine to avoid blocking
	go func() {
		if err := services.RefreshZoneramaNow(zoneramaURL); err != nil {
			logger.Error("Zonerama refresh failed: %v", err)
		} else {
			logger.Info("Zonerama refresh completed successfully")
			// Regenerate flat gallery files for frontend consumption
			if err := services.RegenerateFlatGalleryFiles(); err != nil {
				logger.Error("Failed to regenerate flat gallery files: %v", err)
			}
		}
	}()
	
	c.JSON(http.StatusOK, gin.H{
		"message": "Zonerama refresh started",
		"url":     zoneramaURL,
	})
}

// ProxyImage proxies image requests to Zonerama to avoid CORS issues (public)
func (gc *GalleryController) ProxyImage(c *gin.Context) {
	imageURL := c.Query("url")
	if imageURL == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing image URL"})
		return
	}

	// Validate that the URL is from Zonerama
	if !strings.Contains(imageURL, "zonerama.com") {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid image URL"})
		return
	}

	// Fetch the image
	client := &http.Client{Timeout: 30 * time.Second}
	req, err := http.NewRequest("GET", imageURL, nil)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create request"})
		return
	}
	req.Header.Set("User-Agent", "fotbal-club/1.0")

	resp, err := client.Do(req)
	if err != nil {
		logger.Error("Image fetch failed: %v", err)
		c.JSON(http.StatusBadGateway, gin.H{"error": "Failed to fetch image"})
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		logger.Error("Image fetch returned status %d", resp.StatusCode)
		c.JSON(http.StatusBadGateway, gin.H{"error": "Failed to fetch image"})
		return
	}

	// Copy headers
	for key, values := range resp.Header {
		for _, value := range values {
			c.Header(key, value)
		}
	}

	// Set CORS headers
	c.Header("Access-Control-Allow-Origin", "*")
	c.Header("Access-Control-Allow-Methods", "GET")

	// Stream the image
	c.Status(resp.StatusCode)
	_, err = io.Copy(c.Writer, resp.Body)
	if err != nil {
		logger.Error("Failed to stream image: %v", err)
	}
}
