package store

import (
	"database/sql"
	"errors"
	"fmt"

	"github.com/google/uuid"
)

const stageColumns = `id, project_id, name, tag_pattern, auto_deploy, max_instances, confirm, enable_proxy, promote_from, subdomain, notification_url, created_at, updated_at`

// CreateStage inserts a new stage for a project.
func (s *Store) CreateStage(st Stage) (Stage, error) {
	st.ID = uuid.New().String()
	st.CreatedAt = Now()
	st.UpdatedAt = st.CreatedAt

	_, err := s.db.Exec(
		`INSERT INTO stages (`+stageColumns+`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
		st.ID, st.ProjectID, st.Name, st.TagPattern, BoolToInt(st.AutoDeploy), st.MaxInstances,
		BoolToInt(st.Confirm), BoolToInt(st.EnableProxy), st.PromoteFrom, st.Subdomain, st.NotificationURL, st.CreatedAt, st.UpdatedAt,
	)
	if err != nil {
		return Stage{}, fmt.Errorf("insert stage: %w", err)
	}
	return st, nil
}

// GetStagesByProjectID returns all stages for a given project.
func (s *Store) GetStagesByProjectID(projectID string) ([]Stage, error) {
	rows, err := s.db.Query(
		`SELECT `+stageColumns+` FROM stages WHERE project_id = ? ORDER BY name`, projectID,
	)
	if err != nil {
		return nil, fmt.Errorf("query stages: %w", err)
	}
	defer rows.Close()

	stages := []Stage{}
	for rows.Next() {
		st, err := scanStage(rows)
		if err != nil {
			return nil, err
		}
		stages = append(stages, st)
	}
	return stages, rows.Err()
}

// GetStageByID returns a single stage by its ID.
func (s *Store) GetStageByID(id string) (Stage, error) {
	var st Stage
	var autoDeploy, confirm, enableProxy int
	err := s.db.QueryRow(
		`SELECT `+stageColumns+` FROM stages WHERE id = ?`, id,
	).Scan(&st.ID, &st.ProjectID, &st.Name, &st.TagPattern, &autoDeploy, &st.MaxInstances,
		&confirm, &enableProxy, &st.PromoteFrom, &st.Subdomain, &st.NotificationURL, &st.CreatedAt, &st.UpdatedAt)
	if errors.Is(err, sql.ErrNoRows) {
		return Stage{}, fmt.Errorf("stage %s: %w", id, ErrNotFound)
	}
	if err != nil {
		return Stage{}, fmt.Errorf("query stage: %w", err)
	}
	st.AutoDeploy = autoDeploy != 0
	st.Confirm = confirm != 0
	st.EnableProxy = enableProxy != 0
	return st, nil
}

// UpdateStage updates an existing stage's mutable fields.
func (s *Store) UpdateStage(st Stage) error {
	st.UpdatedAt = Now()
	result, err := s.db.Exec(
		`UPDATE stages SET name=?, tag_pattern=?, auto_deploy=?, max_instances=?, confirm=?, enable_proxy=?, promote_from=?, subdomain=?, notification_url=?, updated_at=?
		 WHERE id=?`,
		st.Name, st.TagPattern, BoolToInt(st.AutoDeploy), st.MaxInstances,
		BoolToInt(st.Confirm), BoolToInt(st.EnableProxy), st.PromoteFrom, st.Subdomain, st.NotificationURL, st.UpdatedAt, st.ID,
	)
	if err != nil {
		return fmt.Errorf("update stage: %w", err)
	}
	n, _ := result.RowsAffected()
	if n == 0 {
		return fmt.Errorf("stage %s: %w", st.ID, ErrNotFound)
	}
	return nil
}

// DeleteStage removes a stage by ID. Cascading deletes handle child instances.
func (s *Store) DeleteStage(id string) error {
	result, err := s.db.Exec(`DELETE FROM stages WHERE id = ?`, id)
	if err != nil {
		return fmt.Errorf("delete stage: %w", err)
	}
	n, _ := result.RowsAffected()
	if n == 0 {
		return fmt.Errorf("stage %s: %w", id, ErrNotFound)
	}
	return nil
}

// BoolToInt converts a bool to an integer for SQLite storage.
func BoolToInt(b bool) int {
	if b {
		return 1
	}
	return 0
}

// scanStage scans a stage row from a *sql.Rows cursor.
func scanStage(rows *sql.Rows) (Stage, error) {
	var st Stage
	var autoDeploy, confirm, enableProxy int
	err := rows.Scan(&st.ID, &st.ProjectID, &st.Name, &st.TagPattern, &autoDeploy, &st.MaxInstances,
		&confirm, &enableProxy, &st.PromoteFrom, &st.Subdomain, &st.NotificationURL, &st.CreatedAt, &st.UpdatedAt)
	if err != nil {
		return Stage{}, fmt.Errorf("scan stage: %w", err)
	}
	st.AutoDeploy = autoDeploy != 0
	st.Confirm = confirm != 0
	st.EnableProxy = enableProxy != 0
	return st, nil
}
