package handlers

import (
	"fmt"
	"net/http"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/rss2/backend/internal/db"
	"github.com/rss2/backend/internal/models"
)

type NewsResponse struct {
	ID                string     `json:"id"`
	Titulo            string     `json:"titulo"`
	Resumen           string     `json:"resumen"`
	URL               string     `json:"url"`
	Fecha             *time.Time `json:"fecha"`
	ImagenURL         *string    `json:"imagen_url"`
	CategoriaID       *int64     `json:"categoria_id"`
	PaisID            *int64     `json:"pais_id"`
	FuenteNombre      string     `json:"fuente_nombre"`
	TitleTranslated   *string    `json:"title_translated"`
	SummaryTranslated *string    `json:"summary_translated"`
	LangTranslated    *string    `json:"lang_translated"`
	Entities          []Entity   `json:"entities,omitempty"`
}

func GetNews(c *gin.Context) {
	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	perPage, _ := strconv.Atoi(c.DefaultQuery("per_page", "30"))
	query := c.Query("q")
	categoryID := c.Query("category_id")
	countryID := c.Query("country_id")
	translatedOnly := c.Query("translated_only") == "true"

	if page < 1 {
		page = 1
	}
	if perPage < 1 || perPage > 100 {
		perPage = 30
	}

	offset := (page - 1) * perPage

	where := "1=1"
	args := []interface{}{}
	argNum := 1

	if query != "" {
		where += fmt.Sprintf(" AND (n.titulo ILIKE $%d OR n.resumen ILIKE $%d)", argNum, argNum)
		args = append(args, "%"+query+"%")
		argNum++
	}
	if categoryID != "" {
		where += fmt.Sprintf(" AND n.categoria_id = $%d", argNum)
		args = append(args, categoryID)
		argNum++
	}
	if countryID != "" {
		where += fmt.Sprintf(" AND n.pais_id = $%d", argNum)
		args = append(args, countryID)
		argNum++
	}
	if translatedOnly {
		where += " AND t.status = 'done' AND t.titulo_trad IS NOT NULL AND t.titulo_trad != n.titulo"
	}

	var total int
	countQuery := fmt.Sprintf("SELECT COUNT(*) FROM noticias n LEFT JOIN traducciones t ON t.noticia_id = n.id AND t.lang_to = 'es' WHERE %s", where)
	err := db.GetPool().QueryRow(c.Request.Context(), countQuery, args...).Scan(&total)
	if err != nil {
		c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: "Failed to count news", Message: err.Error()})
		return
	}

	if total == 0 {
		c.JSON(http.StatusOK, models.NewsListResponse{
			News:       []models.NewsWithTranslations{},
			Total:      0,
			Page:       page,
			PerPage:    perPage,
			TotalPages: 0,
		})
		return
	}

	sqlQuery := fmt.Sprintf(`
		SELECT n.id, n.titulo, COALESCE(n.resumen, ''), n.url, n.fecha, n.imagen_url, 
		       n.categoria_id, n.pais_id, n.fuente_nombre,
		       t.titulo_trad,
		       t.resumen_trad,
		       t.lang_to as lang_trad
		FROM noticias n
		LEFT JOIN traducciones t ON t.noticia_id = n.id AND t.lang_to = 'es'
		WHERE %s
		ORDER BY n.fecha DESC LIMIT $%d OFFSET $%d
	`, where, argNum, argNum+1)

	args = append(args, perPage, offset)

	rows, err := db.GetPool().Query(c.Request.Context(), sqlQuery, args...)
	if err != nil {
		c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: "Failed to fetch news", Message: err.Error()})
		return
	}
	defer rows.Close()

	var newsList []NewsResponse
	for rows.Next() {
		var n NewsResponse
		var imagenURL, fuenteNombre *string
		var categoriaID, paisID *int32

		err := rows.Scan(
			&n.ID, &n.Titulo, &n.Resumen, &n.URL, &n.Fecha, &imagenURL,
			&categoriaID, &paisID, &fuenteNombre,
			&n.TitleTranslated, &n.SummaryTranslated, &n.LangTranslated,
		)
		if err != nil {
			continue
		}
		if imagenURL != nil {
			n.ImagenURL = imagenURL
		}
		if fuenteNombre != nil {
			n.FuenteNombre = *fuenteNombre
		}
		if categoriaID != nil {
			catID := int64(*categoriaID)
			n.CategoriaID = &catID
		}
		if paisID != nil {
			pID := int64(*paisID)
			n.PaisID = &pID
		}
		newsList = append(newsList, n)
	}

	totalPages := (total + perPage - 1) / perPage

	c.JSON(http.StatusOK, gin.H{
		"news":        newsList,
		"total":       total,
		"page":        page,
		"per_page":    perPage,
		"total_pages": totalPages,
	})
}

func GetNewsByID(c *gin.Context) {
	id := c.Param("id")

	sqlQuery := `
		SELECT n.id, n.titulo, COALESCE(n.resumen, ''), n.url, n.fecha, n.imagen_url, 
		       n.categoria_id, n.pais_id, n.fuente_nombre,
		       t.titulo_trad,
		       t.resumen_trad,
		       t.lang_to as lang_trad
		FROM noticias n
		LEFT JOIN traducciones t ON t.noticia_id = n.id AND t.lang_to = 'es'
		WHERE n.id = $1`

	var n NewsResponse
	var imagenURL, fuenteNombre *string
	var categoriaID, paisID *int32

	err := db.GetPool().QueryRow(c.Request.Context(), sqlQuery, id).Scan(
		&n.ID, &n.Titulo, &n.Resumen, &n.URL, &n.Fecha, &imagenURL,
		&categoriaID, &paisID, &fuenteNombre,
		&n.TitleTranslated, &n.SummaryTranslated, &n.LangTranslated,
	)
	if err != nil {
		c.JSON(http.StatusNotFound, models.ErrorResponse{Error: "News not found"})
		return
	}

	if imagenURL != nil {
		n.ImagenURL = imagenURL
	}
	if fuenteNombre != nil {
		n.FuenteNombre = *fuenteNombre
	}
	if categoriaID != nil {
		catID := int64(*categoriaID)
		n.CategoriaID = &catID
	}
	if paisID != nil {
		pID := int64(*paisID)
		n.PaisID = &pID
	}

	// Fetch entities for this news
	entitiesQuery := `
		SELECT t.valor, t.tipo, 1 as cnt, t.wiki_summary, t.wiki_url, t.image_path
		FROM tags_noticia tn
		JOIN tags t ON tn.tag_id = t.id
		JOIN traducciones tr ON tn.traduccion_id = tr.id
		WHERE tr.noticia_id = $1 AND t.tipo IN ('persona', 'organizacion')
	`
	rows, err := db.GetPool().Query(c.Request.Context(), entitiesQuery, id)
	var entities []Entity
	if err == nil {
		defer rows.Close()
		for rows.Next() {
			var e Entity
			if err := rows.Scan(&e.Valor, &e.Tipo, &e.Count, &e.WikiSummary, &e.WikiURL, &e.ImagePath); err == nil {
				entities = append(entities, e)
			}
		}
	}
	if entities == nil {
		entities = []Entity{}
	}
	n.Entities = entities

	c.JSON(http.StatusOK, n)
}

func DeleteNews(c *gin.Context) {
	id := c.Param("id")

	result, err := db.GetPool().Exec(c.Request.Context(), "DELETE FROM noticias WHERE id = $1", id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: "Failed to delete news", Message: err.Error()})
		return
	}

	if result.RowsAffected() == 0 {
		c.JSON(http.StatusNotFound, models.ErrorResponse{Error: "News not found"})
		return
	}

	c.JSON(http.StatusOK, models.SuccessResponse{Message: "News deleted successfully"})
}

type Entity struct {
	Valor       string  `json:"valor"`
	Tipo        string  `json:"tipo"`
	Count       int     `json:"count"`
	WikiSummary *string `json:"wiki_summary"`
	WikiURL     *string `json:"wiki_url"`
	ImagePath   *string `json:"image_path"`
}

type EntityListResponse struct {
	Entities   []Entity `json:"entities"`
	Total      int      `json:"total"`
	Page       int      `json:"page"`
	PerPage    int      `json:"per_page"`
	TotalPages int      `json:"total_pages"`
}

func GetEntities(c *gin.Context) {
	countryID := c.Query("country_id")
	categoryID := c.Query("category_id")
	entityType := c.DefaultQuery("tipo", "persona")
	
	q := c.Query("q")
	
	pageStr := c.DefaultQuery("page", "1")
	perPageStr := c.DefaultQuery("per_page", "50")

	page, _ := strconv.Atoi(pageStr)
	perPage, _ := strconv.Atoi(perPageStr)

	if page < 1 {
		page = 1
	}
	if perPage < 1 || perPage > 100 {
		perPage = 50
	}

	offset := (page - 1) * perPage

	where := "t.tipo = $1"
	args := []interface{}{entityType}

	if countryID != "" {
		where += fmt.Sprintf(" AND n.pais_id = $%d", len(args)+1)
		args = append(args, countryID)
	}

	if categoryID != "" {
		where += fmt.Sprintf(" AND n.categoria_id = $%d", len(args)+1)
		args = append(args, categoryID)
	}
	
	if q != "" {
		where += fmt.Sprintf(" AND COALESCE(ea.canonical_name, t.valor) ILIKE $%d", len(args)+1)
		args = append(args, "%"+q+"%")
	}

	// 1. Get the total count of distinct canonical entities matching the filter
	countQuery := fmt.Sprintf(`
		SELECT COUNT(DISTINCT COALESCE(ea.canonical_name, t.valor))
		FROM tags_noticia tn
		JOIN tags t ON tn.tag_id = t.id
		JOIN traducciones tr ON tn.traduccion_id = tr.id
		JOIN noticias n ON tr.noticia_id = n.id
		LEFT JOIN entity_aliases ea ON LOWER(ea.alias) = LOWER(t.valor) AND ea.tipo = t.tipo
		WHERE %s
	`, where)

	var total int
	err := db.GetPool().QueryRow(c.Request.Context(), countQuery, args...).Scan(&total)
	if err != nil {
		c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: "Failed to get entities count", Message: err.Error()})
		return
	}

	if total == 0 {
		c.JSON(http.StatusOK, EntityListResponse{
			Entities:   []Entity{},
			Total:      0,
			Page:       page,
			PerPage:    perPage,
			TotalPages: 0,
		})
		return
	}

	// 2. Fetch the paginated entities
	args = append(args, perPage, offset)
	query := fmt.Sprintf(`
		SELECT COALESCE(ea.canonical_name, t.valor) as valor, t.tipo, COUNT(*)::int as cnt,
		       MAX(t.wiki_summary), MAX(t.wiki_url), MAX(t.image_path)
		FROM tags_noticia tn
		JOIN tags t ON tn.tag_id = t.id
		JOIN traducciones tr ON tn.traduccion_id = tr.id
		JOIN noticias n ON tr.noticia_id = n.id
		LEFT JOIN entity_aliases ea ON LOWER(ea.alias) = LOWER(t.valor) AND ea.tipo = t.tipo
		WHERE %s
		GROUP BY COALESCE(ea.canonical_name, t.valor), t.tipo
		ORDER BY cnt DESC
		LIMIT $%d OFFSET $%d
	`, where, len(args)-1, len(args))

	rows, err := db.GetPool().Query(c.Request.Context(), query, args...)
	if err != nil {
		c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: "Failed to get entities", Message: err.Error()})
		return
	}
	defer rows.Close()

	var entities []Entity
	for rows.Next() {
		var e Entity
		if err := rows.Scan(&e.Valor, &e.Tipo, &e.Count, &e.WikiSummary, &e.WikiURL, &e.ImagePath); err != nil {
			continue
		}
		entities = append(entities, e)
	}

	if entities == nil {
		entities = []Entity{}
	}

	totalPages := (total + perPage - 1) / perPage

	c.JSON(http.StatusOK, EntityListResponse{
		Entities:   entities,
		Total:      total,
		Page:       page,
		PerPage:    perPage,
		TotalPages: totalPages,
	})
}
