package repository

import (
	"git.mchus.pro/mchus/quoteforge/internal/models"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

type ProjectRepository struct {
	db *gorm.DB
}

func NewProjectRepository(db *gorm.DB) *ProjectRepository {
	return &ProjectRepository{db: db}
}

func (r *ProjectRepository) Create(project *models.Project) error {
	return r.db.Create(project).Error
}

func (r *ProjectRepository) Update(project *models.Project) error {
	return r.db.Save(project).Error
}

func (r *ProjectRepository) UpsertByUUID(project *models.Project) error {
	if err := r.db.Clauses(clause.OnConflict{
		Columns: []clause.Column{{Name: "uuid"}},
		DoUpdates: clause.AssignmentColumns([]string{
			"owner_username",
			"code",
			"variant",
			"name",
			"tracker_url",
			"is_active",
			"is_system",
			"updated_at",
		}),
	}).Create(project).Error; err != nil {
		return err
	}

	// Ensure caller always gets canonical server ID.
	var persisted models.Project
	if err := r.db.Where("uuid = ?", project.UUID).First(&persisted).Error; err != nil {
		return err
	}
	project.ID = persisted.ID
	return nil
}

func (r *ProjectRepository) GetByUUID(uuid string) (*models.Project, error) {
	var project models.Project
	if err := r.db.Where("uuid = ?", uuid).First(&project).Error; err != nil {
		return nil, err
	}
	return &project, nil
}

func (r *ProjectRepository) GetSystemByOwner(ownerUsername string) (*models.Project, error) {
	var project models.Project
	if err := r.db.Where("owner_username = ? AND is_system = ? AND name = ?", ownerUsername, true, "Без проекта").
		First(&project).Error; err != nil {
		return nil, err
	}
	return &project, nil
}

func (r *ProjectRepository) List(offset, limit int, includeArchived bool) ([]models.Project, int64, error) {
	var projects []models.Project
	var total int64

	query := r.db.Model(&models.Project{})
	if !includeArchived {
		query = query.Where("is_active = ?", true)
	}

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	if err := query.Order("created_at DESC").Offset(offset).Limit(limit).Find(&projects).Error; err != nil {
		return nil, 0, err
	}

	return projects, total, nil
}

func (r *ProjectRepository) ListByOwner(ownerUsername string, includeArchived bool) ([]models.Project, error) {
	var projects []models.Project

	query := r.db.Where("owner_username = ?", ownerUsername)
	if !includeArchived {
		query = query.Where("is_active = ?", true)
	}

	if err := query.Order("created_at DESC").Find(&projects).Error; err != nil {
		return nil, err
	}
	return projects, nil
}

func (r *ProjectRepository) Archive(uuid string) error {
	return r.db.Model(&models.Project{}).Where("uuid = ?", uuid).Update("is_active", false).Error
}

func (r *ProjectRepository) Reactivate(uuid string) error {
	return r.db.Model(&models.Project{}).Where("uuid = ?", uuid).Update("is_active", true).Error
}

// PurgeEmptyNamelessProjects removes service-trash projects that have no configurations attached:
// 1) projects with empty names;
// 2) duplicate "Без проекта" rows without configurations (case-insensitive, trimmed).
func (r *ProjectRepository) PurgeEmptyNamelessProjects() (int64, error) {
	tx := r.db.Exec(`
DELETE p
FROM qt_projects p
WHERE (
      TRIM(COALESCE(p.name, '')) = ''
      OR LOWER(TRIM(COALESCE(p.name, ''))) = LOWER('Без проекта')
)
  AND NOT EXISTS (
      SELECT 1
      FROM qt_configurations c
      WHERE c.project_uuid = p.uuid
  )`)
	return tx.RowsAffected, tx.Error
}

// EnsureSystemProjectsAndBackfillConfigurations ensures there is a single shared system project
// named "Без проекта", reassigns orphan/legacy links to it and removes duplicates.
func (r *ProjectRepository) EnsureSystemProjectsAndBackfillConfigurations() error {
	return r.db.Transaction(func(tx *gorm.DB) error {
		type row struct {
			UUID string `gorm:"column:uuid"`
		}
		var canonical row
		err := tx.Raw(`
SELECT uuid
FROM qt_projects
WHERE LOWER(TRIM(COALESCE(name, ''))) = LOWER('Без проекта')
  AND is_system = TRUE
ORDER BY CASE WHEN TRIM(COALESCE(owner_username, '')) = '' THEN 0 ELSE 1 END, created_at ASC, id ASC
LIMIT 1`).Scan(&canonical).Error
		if err != nil {
			return err
		}
		if canonical.UUID == "" {
			if err := tx.Exec(`
INSERT INTO qt_projects (uuid, owner_username, name, is_active, is_system, created_at, updated_at)
VALUES (UUID(), '', 'Без проекта', TRUE, TRUE, NOW(), NOW())`).Error; err != nil {
				return err
			}
			if err := tx.Raw(`
SELECT uuid
FROM qt_projects
WHERE LOWER(TRIM(COALESCE(name, ''))) = LOWER('Без проекта')
ORDER BY created_at DESC, id DESC
LIMIT 1`).Scan(&canonical).Error; err != nil {
				return err
			}
			if canonical.UUID == "" {
				return gorm.ErrRecordNotFound
			}
		}

		if err := tx.Exec(`
UPDATE qt_projects
SET name = 'Без проекта',
    is_active = TRUE,
    is_system = TRUE
WHERE uuid = ?`, canonical.UUID).Error; err != nil {
			return err
		}

		if err := tx.Exec(`
UPDATE qt_configurations
SET project_uuid = ?
WHERE project_uuid IS NULL OR project_uuid = ''`, canonical.UUID).Error; err != nil {
			return err
		}

		if err := tx.Exec(`
UPDATE qt_configurations c
JOIN qt_projects p ON p.uuid = c.project_uuid
SET c.project_uuid = ?
WHERE LOWER(TRIM(COALESCE(p.name, ''))) = LOWER('Без проекта')
  AND p.uuid <> ?`, canonical.UUID, canonical.UUID).Error; err != nil {
			return err
		}

		return tx.Exec(`
DELETE FROM qt_projects
WHERE LOWER(TRIM(COALESCE(name, ''))) = LOWER('Без проекта')
  AND uuid <> ?`, canonical.UUID).Error
	})
}
