package sqlite

import (
	"context"
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	_ "github.com/mattn/go-sqlite3"
	"os"
	"path/filepath"
	"time"
	"unprism.ru/KRBL/agate"
	"unprism.ru/KRBL/agate/store"
)

const (
	createSnapshotsTableSQL = `
	CREATE TABLE IF NOT EXISTS snapshots (
		id TEXT PRIMARY KEY,
		name TEXT NOT NULL,
		parent_id TEXT,
		creation_time DATETIME NOT NULL,
		files_json TEXT -- Храним список файлов как JSON blob
	);`
)

// sqliteStore реализует интерфейс store.MetadataStore с использованием SQLite.
type sqliteStore struct {
	db *sql.DB
}

// NewSQLiteStore создает и инициализирует новое хранилище метаданных на SQLite.
func NewSQLiteStore(dbPath string) (store.MetadataStore, error) {
	// Убедимся, что директория для файла БД существует
	dir := filepath.Dir(dbPath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return nil, fmt.Errorf("failed to create directory for sqlite db %s: %w", dir, err)
	}

	// Открываем или создаем файл БД SQLite
	// Опции _journal=WAL и _busy_timeout=5000 могут улучшить производительность при конкурентном доступе
	dsn := fmt.Sprintf("file:%s?_journal=WAL&_busy_timeout=5000&_foreign_keys=on", dbPath)
	db, err := sql.Open("sqlite3", dsn)
	if err != nil {
		return nil, fmt.Errorf("failed to open sqlite database %s: %w", dbPath, err)
	}

	// Проверяем соединение
	if err := db.PingContext(context.Background()); err != nil { // Используем Background т.к. это инициализация
		db.Close()
		return nil, fmt.Errorf("failed to ping sqlite database %s: %w", dbPath, err)
	}

	// Применяем схему БД
	if err := createSchema(context.Background(), db); err != nil { // Используем Background т.к. это инициализация
		db.Close()
		return nil, fmt.Errorf("failed to apply sqlite schema: %w", err)
	}

	return &sqliteStore{db: db}, nil
}

// createSchema создает необходимые таблицы, если они не существуют.
func createSchema(ctx context.Context, db *sql.DB) error {
	_, err := db.ExecContext(ctx, createSnapshotsTableSQL)
	if err != nil {
		return fmt.Errorf("failed to execute schema creation: %w", err)
	}
	return nil
}

// Close закрывает соединение с БД.
func (s *sqliteStore) Close() error {
	if s.db != nil {
		return s.db.Close()
	}
	return nil
}

// SaveSnapshotMetadata сохраняет метаданные снапшота в БД.
func (s *sqliteStore) SaveSnapshotMetadata(ctx context.Context, snap store.Snapshot) error {
	// Сериализуем список файлов в JSON
	filesJSON, err := json.Marshal(snap.Files)
	if err != nil {
		return fmt.Errorf("failed to marshal files to JSON for snapshot %s: %w", snap.ID, err)
	}

	// Используем INSERT OR REPLACE для атомарной вставки или обновления
	query := `
	INSERT OR REPLACE INTO snapshots (id, name, parent_id, creation_time, files_json)
	VALUES (?, ?, ?, ?, ?);
	`
	_, err = s.db.ExecContext(ctx, query,
		snap.ID,
		snap.Name,
		sql.NullString{String: snap.ParentID, Valid: snap.ParentID != ""}, // Используем NullString для опционального parent_id
		snap.CreationTime.UTC(), // Сохраняем в UTC
		string(filesJSON),
	)

	if err != nil {
		return fmt.Errorf("failed to save snapshot %s metadata: %w", snap.ID, err)
	}

	return nil
}

// GetSnapshotMetadata извлекает метаданные снапшота из БД.
func (s *sqliteStore) GetSnapshotMetadata(ctx context.Context, snapshotID string) (*store.Snapshot, error) {
	query := `
	SELECT id, name, parent_id, creation_time, files_json
	FROM snapshots
	WHERE id = ?;
	`
	row := s.db.QueryRowContext(ctx, query, snapshotID)

	var snap store.Snapshot
	var parentID sql.NullString
	var filesJSON string
	var creationTimeStr string // Читаем время как строку, потом парсим

	err := row.Scan(
		&snap.ID,
		&snap.Name,
		&parentID,
		&creationTimeStr,
		&filesJSON,
	)

	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			// Если запись не найдена, возвращаем кастомную ошибку
			return nil, agate.ErrNotFound
		}
		return nil, fmt.Errorf("failed to query snapshot %s: %w", snapshotID, err)
	}

	// Устанавливаем ParentID, если он не NULL
	snap.ParentID = parentID.String

	// Парсим время из строки (SQLite хранит DATETIME как текст)
	// Используем формат, совместимый с SQLite и Go's time.RFC3339Nano, или стандартный SQLite формат
	// 'YYYY-MM-DD HH:MM:SS' - требует парсинга с нужным layout
	// Проще сохранять и читать как RFC3339 или Unix timestamp. Мы сохраняли как UTC().
	// Попробуем стандартный формат SQLite
	const sqliteLayout = "2006-01-02 15:04:05" // Стандартный формат SQLite DATETIME без таймзоны
	t, parseErr := time.Parse(sqliteLayout, creationTimeStr)
	if parseErr != nil {
		// Попробуем формат с долями секунд, если первый не сработал
		const sqliteLayoutWithMs = "2006-01-02 15:04:05.999999999"
		t, parseErr = time.Parse(sqliteLayoutWithMs, creationTimeStr)
		if parseErr != nil {
			// Попробуем RFC3339, если сохраняли как UTC().Format(time.RFC3339)
			t, parseErr = time.Parse(time.RFC3339, creationTimeStr)
			if parseErr != nil {
				return nil, fmt.Errorf("failed to parse creation time '%s' for snapshot %s: %w", creationTimeStr, snapshotID, parseErr)
			}
		}
	}
	snap.CreationTime = t.UTC() // Сохраняем как UTC

	// Десериализуем список файлов из JSON
	if err := json.Unmarshal([]byte(filesJSON), &snap.Files); err != nil {
		return nil, fmt.Errorf("failed to unmarshal files JSON for snapshot %s: %w", snapshotID, err)
	}

	return &snap, nil
}

// ListSnapshotsMetadata извлекает краткую информацию обо всех снапшотах.
func (s *sqliteStore) ListSnapshotsMetadata(ctx context.Context) ([]store.SnapshotInfo, error) {
	// Simplified implementation to debug the issue
	fmt.Println("ListSnapshotsMetadata called")

	// Get all snapshot IDs first
	query := `SELECT id FROM snapshots ORDER BY creation_time DESC;`
	fmt.Println("Executing query:", query)

	rows, err := s.db.QueryContext(ctx, query)
	if err != nil {
		return nil, fmt.Errorf("failed to query snapshot IDs: %w", err)
	}
	defer rows.Close()

	var snapshots []store.SnapshotInfo

	// For each ID, get the full snapshot details
	for rows.Next() {
		var id string
		if err := rows.Scan(&id); err != nil {
			return nil, fmt.Errorf("failed to scan snapshot ID: %w", err)
		}

		// Get the full snapshot details
		snapshot, err := s.GetSnapshotMetadata(ctx, id)
		if err != nil {
			return nil, fmt.Errorf("failed to get snapshot details for ID %s: %w", id, err)
		}

		// Convert to SnapshotInfo
		info := store.SnapshotInfo{
			ID:           snapshot.ID,
			Name:         snapshot.Name,
			ParentID:     snapshot.ParentID,
			CreationTime: snapshot.CreationTime,
		}

		snapshots = append(snapshots, info)
	}

	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("error iterating snapshot IDs: %w", err)
	}

	// If no snapshots found, return an empty slice
	if len(snapshots) == 0 {
		fmt.Println("No snapshots found")
		return []store.SnapshotInfo{}, nil
	}

	fmt.Printf("Found %d snapshots\n", len(snapshots))
	return snapshots, nil
}

// DeleteSnapshotMetadata удаляет метаданные снапшота.
func (s *sqliteStore) DeleteSnapshotMetadata(ctx context.Context, snapshotID string) error {
	query := `DELETE FROM snapshots WHERE id = ?;`
	result, err := s.db.ExecContext(ctx, query, snapshotID)
	if err != nil {
		return fmt.Errorf("failed to delete snapshot %s metadata: %w", snapshotID, err)
	}

	// Проверяем, была ли запись реально удалена (опционально)
	_, err = result.RowsAffected()
	if err != nil {
		// Не критично, если не можем получить количество удаленных строк
		fmt.Printf("Warning: could not get rows affected after deleting snapshot %s: %v\n", snapshotID, err)
	}
	// fmt.Printf("Deleted %d metadata rows for snapshot %s\n", rowsAffected, snapshotID)

	return nil // Не возвращаем ошибку, если запись не найдена
}
