package models

import (
	"github.com/sujit-baniya/fiber-boilerplate/app"
	"gorm.io/gorm"
)

// DocPagina es el contenido real de documentación.
// Visibilidad "public" → accesible sin login.
// Visibilidad "private" → requiere login Y tener al menos uno de los roles asignados.
// TipoContenido "markdown" | "html" determina cómo se renderiza el campo Contenido.
type DocPagina struct {
	gorm.Model
	SaasID        uint         `json:"saas_id"        gorm:"column:saas_id;not null"`
	Saas          SaasProducto `json:"saas"           gorm:"foreignKey:SaasID"`
	CategoriaID   uint         `json:"categoria_id"   gorm:"column:categoria_id;not null"`
	Categoria     DocCategoria `json:"categoria"      gorm:"foreignKey:CategoriaID"`
	Titulo        string       `json:"titulo"         gorm:"column:titulo;not null"`
	Slug          string       `json:"slug"           gorm:"column:slug;not null;uniqueIndex:idx_saas_slug"`
	Contenido     string       `json:"contenido"      gorm:"column:contenido;type:text"`
	TipoContenido string       `json:"tipo_contenido" gorm:"column:tipo_contenido;default:'markdown'"` // markdown | html
	Visibilidad   string       `json:"visibilidad"    gorm:"column:visibilidad;default:'public'"`       // public | private
	Orden         int          `json:"orden"          gorm:"column:orden;default:0"`
	Publicado     bool         `json:"publicado"      gorm:"column:publicado;default:false"`
	CreadoPor     uint         `json:"creado_por"     gorm:"column:creado_por"`
	// Roles que pueden ver esta página cuando visibilidad = "private"
	Roles []Roles `json:"roles" gorm:"many2many:doc_pagina_roles"`
}

func (DocPagina) TableName() string { return "doc_paginas" }

// ─── Queries ──────────────────────────────────────────────────────────────────

func GetAllDocPaginas(limit, offset int, search string, saasID, categoriaID uint) ([]DocPagina, int64, error) {
	var items []DocPagina
	var total int64
	db := app.Http.Database.DB.Model(&DocPagina{}).
		Preload("Saas").Preload("Categoria").Preload("Roles")
	if search != "" {
		db = db.Where("titulo ILIKE ?", "%"+search+"%")
	}
	if saasID > 0 {
		db = db.Where("saas_id = ?", saasID)
	}
	if categoriaID > 0 {
		db = db.Where("categoria_id = ?", categoriaID)
	}
	if err := db.Count(&total).Error; err != nil {
		return nil, 0, err
	}
	if err := db.Order("saas_id ASC, orden ASC, created_at DESC").Limit(limit).Offset(offset).Find(&items).Error; err != nil {
		return nil, 0, err
	}
	return items, total, nil
}

// GetDocPaginasPublicas devuelve páginas publicadas y públicas de un SaaS, agrupables por categoría.
func GetDocPaginasPublicas(saasID uint) ([]DocPagina, error) {
	var items []DocPagina
	if err := app.Http.Database.DB.
		Preload("Categoria").
		Where("saas_id = ? AND visibilidad = 'public' AND publicado = true", saasID).
		Order("orden ASC, titulo ASC").
		Find(&items).Error; err != nil {
		return nil, err
	}
	return items, nil
}

// GetDocPaginasParaRoles devuelve páginas publicadas accesibles por los roles dados (incluye public + private con rol).
func GetDocPaginasParaRoles(saasID uint, rolNames []string) ([]DocPagina, error) {
	var items []DocPagina
	db := app.Http.Database.DB.Preload("Categoria").Preload("Roles")

	// Páginas públicas del SaaS
	if err := db.Where("saas_id = ? AND publicado = true AND visibilidad = 'public'", saasID).
		Order("orden ASC, titulo ASC").Find(&items).Error; err != nil {
		return nil, err
	}

	// Páginas privadas accesibles por los roles del usuario
	if len(rolNames) > 0 {
		var privadas []DocPagina
		if err := app.Http.Database.DB.Preload("Categoria").Preload("Roles").
			Joins("JOIN doc_pagina_roles dpr ON dpr.doc_pagina_id = doc_paginas.id").
			Joins("JOIN roles r ON r.id = dpr.roles_id").
			Where("doc_paginas.saas_id = ? AND doc_paginas.publicado = true AND doc_paginas.visibilidad = 'private' AND r.name IN ?", saasID, rolNames).
			Group("doc_paginas.id").
			Order("doc_paginas.orden ASC, doc_paginas.titulo ASC").
			Find(&privadas).Error; err != nil {
			return nil, err
		}
		items = append(items, privadas...)
	}
	return items, nil
}

func GetDocPaginaByID(id uint) (*DocPagina, error) {
	var item DocPagina
	if err := app.Http.Database.DB.Preload("Saas").Preload("Categoria").Preload("Roles").First(&item, id).Error; err != nil {
		return nil, err
	}
	return &item, nil
}

func GetDocPaginaBySlug(saasID uint, slug string) (*DocPagina, error) {
	var item DocPagina
	if err := app.Http.Database.DB.Preload("Saas").Preload("Categoria").Preload("Roles").
		Where("saas_id = ? AND slug = ?", saasID, slug).
		First(&item).Error; err != nil {
		return nil, err
	}
	return &item, nil
}

func CreateDocPagina(p *DocPagina, rolIDs []uint) error {
	db := app.Http.Database.DB
	if err := db.Omit("Roles.*").Create(p).Error; err != nil {
		return err
	}
	return syncDocPaginaRoles(p.ID, rolIDs)
}

func UpdateDocPagina(p *DocPagina, rolIDs []uint) error {
	db := app.Http.Database.DB
	if err := db.Model(&DocPagina{}).Where("id = ?", p.ID).Updates(map[string]interface{}{
		"saas_id":        p.SaasID,
		"categoria_id":   p.CategoriaID,
		"titulo":         p.Titulo,
		"slug":           p.Slug,
		"contenido":      p.Contenido,
		"tipo_contenido": p.TipoContenido,
		"visibilidad":    p.Visibilidad,
		"orden":          p.Orden,
		"publicado":      p.Publicado,
	}).Error; err != nil {
		return err
	}
	return syncDocPaginaRoles(p.ID, rolIDs)
}

func DeleteDocPagina(id uint) error {
	db := app.Http.Database.DB
	// Limpiar la tabla join antes de borrar el registro
	if err := db.Exec("DELETE FROM doc_pagina_roles WHERE doc_pagina_id = ?", id).Error; err != nil {
		return err
	}
	return db.Delete(&DocPagina{}, id).Error
}

// syncDocPaginaRoles sincroniza los roles de una página usando SQL directo.
func syncDocPaginaRoles(paginaID uint, rolIDs []uint) error {
	db := app.Http.Database.DB
	if err := db.Exec("DELETE FROM doc_pagina_roles WHERE doc_pagina_id = ?", paginaID).Error; err != nil {
		return err
	}
	for _, rid := range rolIDs {
		if err := db.Exec(
			"INSERT INTO doc_pagina_roles (doc_pagina_id, roles_id) VALUES (?, ?) ON CONFLICT DO NOTHING",
			paginaID, rid,
		).Error; err != nil {
			return err
		}
	}
	return nil
}
