package store

import (
	"ci/internal/model"
	"os"
	"path/filepath"

	"github.com/glebarez/sqlite"
	"gorm.io/gorm"
)

// Store wraps the database connection and provides data access methods.
type Store struct {
	DB *gorm.DB
}

// New creates a new Store, initializes the database, and runs migrations.
func New(dataDir string) (*Store, error) {
	if err := os.MkdirAll(dataDir, 0755); err != nil {
		return nil, err
	}

	dbPath := filepath.Join(dataDir, "ci.db")
	db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
	if err != nil {
		return nil, err
	}

	if err := db.AutoMigrate(&model.Project{}, &model.BuildRecord{}, &model.DeployConfig{}, &model.DeployRecord{}); err != nil {
		return nil, err
	}

	return &Store{DB: db}, nil
}

// --- Project CRUD ---

func (s *Store) CreateProject(p *model.Project) error {
	return s.DB.Create(p).Error
}

func (s *Store) ListProjects() ([]model.Project, error) {
	var projects []model.Project
	err := s.DB.Order("created_at desc").Find(&projects).Error
	return projects, err
}

func (s *Store) GetProject(id uint) (*model.Project, error) {
	var p model.Project
	err := s.DB.First(&p, id).Error
	if err != nil {
		return nil, err
	}
	return &p, nil
}

func (s *Store) UpdateProject(p *model.Project) error {
	return s.DB.Save(p).Error
}

func (s *Store) DeleteProject(id uint) error {
	return s.DB.Delete(&model.Project{}, id).Error
}

// --- BuildRecord CRUD ---

func (s *Store) CreateBuildRecord(r *model.BuildRecord) error {
	return s.DB.Create(r).Error
}

func (s *Store) UpdateBuildRecord(r *model.BuildRecord) error {
	return s.DB.Save(r).Error
}

func (s *Store) GetBuildRecord(id uint) (*model.BuildRecord, error) {
	var r model.BuildRecord
	err := s.DB.Preload("Project").First(&r, id).Error
	if err != nil {
		return nil, err
	}
	return &r, nil
}

func (s *Store) ListBuildRecords(projectID uint, limit int) ([]model.BuildRecord, error) {
	var records []model.BuildRecord
	if limit <= 0 {
		limit = 20
	}
	err := s.DB.Where("project_id = ?", projectID).
		Order("started_at desc").
		Limit(limit).
		Find(&records).Error
	return records, err
}

// --- DeployConfig CRUD ---

// GetDeployConfig returns the deploy config for a project, or nil if not configured.
func (s *Store) GetDeployConfig(projectID uint) (*model.DeployConfig, error) {
	var cfg model.DeployConfig
	err := s.DB.Where("project_id = ?", projectID).First(&cfg).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil // not configured is not an error
		}
		return nil, err
	}
	return &cfg, nil
}

// UpsertDeployConfig creates or updates the deploy config for a project.
func (s *Store) UpsertDeployConfig(cfg *model.DeployConfig) error {
	// Use FirstOrCreate + Assign to handle upsert
	var existing model.DeployConfig
	err := s.DB.Where("project_id = ?", cfg.ProjectID).First(&existing).Error
	if err == nil {
		// Update existing
		cfg.ID = existing.ID
		cfg.CreatedAt = existing.CreatedAt
		return s.DB.Save(cfg).Error
	}
	// Create new
	return s.DB.Create(cfg).Error
}

// DeleteDeployConfig removes the deploy config for a project.
func (s *Store) DeleteDeployConfig(projectID uint) error {
	return s.DB.Where("project_id = ?", projectID).Delete(&model.DeployConfig{}).Error
}

// --- DeployRecord CRUD ---

// CreateDeployRecord creates a new deploy record.
func (s *Store) CreateDeployRecord(r *model.DeployRecord) error {
	return s.DB.Create(r).Error
}

// UpdateDeployRecord updates an existing deploy record.
func (s *Store) UpdateDeployRecord(r *model.DeployRecord) error {
	return s.DB.Save(r).Error
}

// GetDeployRecord returns a single deploy record by ID.
func (s *Store) GetDeployRecord(id uint) (*model.DeployRecord, error) {
	var r model.DeployRecord
	err := s.DB.Preload("Project").First(&r, id).Error
	if err != nil {
		return nil, err
	}
	return &r, nil
}

// ListDeployRecords returns deploy history for a project.
func (s *Store) ListDeployRecords(projectID uint, limit int) ([]model.DeployRecord, error) {
	var records []model.DeployRecord
	if limit <= 0 {
		limit = 20
	}
	err := s.DB.Where("project_id = ?", projectID).
		Order("started_at desc").
		Limit(limit).
		Find(&records).Error
	return records, err
}
