package store

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

	"github.com/google/uuid"
)

// CreateVolumeSnapshot inserts a snapshot metadata record. ID is generated
// when empty; CreatedAt is stamped server-side.
func (s *Store) CreateVolumeSnapshot(v VolumeSnapshot) (VolumeSnapshot, error) {
	if v.WorkloadID == "" || v.Filename == "" {
		return VolumeSnapshot{}, fmt.Errorf("volume_snapshot: workload_id and filename are required")
	}
	if v.ID == "" {
		v.ID = uuid.New().String()
	}
	if v.Manifest == "" {
		v.Manifest = "[]"
	}
	v.CreatedAt = Now()

	if _, err := s.db.Exec(
		`INSERT INTO volume_snapshots (id, workload_id, label, filename, size_bytes, manifest, created_at)
		 VALUES (?, ?, ?, ?, ?, ?, ?)`,
		v.ID, v.WorkloadID, v.Label, v.Filename, v.SizeBytes, v.Manifest, v.CreatedAt,
	); err != nil {
		return VolumeSnapshot{}, fmt.Errorf("insert volume snapshot: %w", err)
	}
	return v, nil
}

// GetVolumeSnapshot returns one snapshot by ID.
func (s *Store) GetVolumeSnapshot(id string) (VolumeSnapshot, error) {
	var v VolumeSnapshot
	err := s.db.QueryRow(
		`SELECT id, workload_id, label, filename, size_bytes, manifest, created_at
		 FROM volume_snapshots WHERE id = ?`, id,
	).Scan(&v.ID, &v.WorkloadID, &v.Label, &v.Filename, &v.SizeBytes, &v.Manifest, &v.CreatedAt)
	if errors.Is(err, sql.ErrNoRows) {
		return VolumeSnapshot{}, fmt.Errorf("volume snapshot %s: %w", id, ErrNotFound)
	}
	if err != nil {
		return VolumeSnapshot{}, fmt.Errorf("query volume snapshot: %w", err)
	}
	return v, nil
}

// ListVolumeSnapshots returns a workload's snapshots, newest first.
func (s *Store) ListVolumeSnapshots(workloadID string) ([]VolumeSnapshot, error) {
	rows, err := s.db.Query(
		`SELECT id, workload_id, label, filename, size_bytes, manifest, created_at
		 FROM volume_snapshots WHERE workload_id = ? ORDER BY created_at DESC`, workloadID,
	)
	if err != nil {
		return nil, fmt.Errorf("query volume snapshots: %w", err)
	}
	defer rows.Close()

	out := []VolumeSnapshot{}
	for rows.Next() {
		v, err := scanVolumeSnapshot(rows)
		if err != nil {
			return nil, err
		}
		out = append(out, v)
	}
	return out, rows.Err()
}

// DeleteVolumeSnapshot removes one snapshot row by ID.
func (s *Store) DeleteVolumeSnapshot(id string) error {
	result, err := s.db.Exec(`DELETE FROM volume_snapshots WHERE id = ?`, id)
	if err != nil {
		return fmt.Errorf("delete volume snapshot: %w", err)
	}
	if n, _ := result.RowsAffected(); n == 0 {
		return fmt.Errorf("volume snapshot %s: %w", id, ErrNotFound)
	}
	return nil
}

// CountVolumeSnapshots returns how many snapshots a workload has.
func (s *Store) CountVolumeSnapshots(workloadID string) (int, error) {
	var n int
	if err := s.db.QueryRow(
		`SELECT COUNT(*) FROM volume_snapshots WHERE workload_id = ?`, workloadID,
	).Scan(&n); err != nil {
		return 0, fmt.Errorf("count volume snapshots: %w", err)
	}
	return n, nil
}

// GetOldestVolumeSnapshots returns the N oldest snapshots for a workload, for
// retention pruning.
func (s *Store) GetOldestVolumeSnapshots(workloadID string, limit int) ([]VolumeSnapshot, error) {
	rows, err := s.db.Query(
		`SELECT id, workload_id, label, filename, size_bytes, manifest, created_at
		 FROM volume_snapshots WHERE workload_id = ? ORDER BY created_at ASC LIMIT ?`, workloadID, limit,
	)
	if err != nil {
		return nil, fmt.Errorf("query oldest volume snapshots: %w", err)
	}
	defer rows.Close()

	out := []VolumeSnapshot{}
	for rows.Next() {
		v, err := scanVolumeSnapshot(rows)
		if err != nil {
			return nil, err
		}
		out = append(out, v)
	}
	return out, rows.Err()
}

// AllVolumeSnapshotFilenames returns every snapshot archive filename across all
// workloads, for orphan-file reconciliation at startup.
func (s *Store) AllVolumeSnapshotFilenames() ([]string, error) {
	rows, err := s.db.Query(`SELECT filename FROM volume_snapshots`)
	if err != nil {
		return nil, fmt.Errorf("query snapshot filenames: %w", err)
	}
	defer rows.Close()

	out := []string{}
	for rows.Next() {
		var name string
		if err := rows.Scan(&name); err != nil {
			return nil, fmt.Errorf("scan snapshot filename: %w", err)
		}
		out = append(out, name)
	}
	return out, rows.Err()
}

func scanVolumeSnapshot(rows *sql.Rows) (VolumeSnapshot, error) {
	var v VolumeSnapshot
	if err := rows.Scan(&v.ID, &v.WorkloadID, &v.Label, &v.Filename,
		&v.SizeBytes, &v.Manifest, &v.CreatedAt); err != nil {
		return VolumeSnapshot{}, fmt.Errorf("scan volume snapshot: %w", err)
	}
	return v, nil
}
