package db

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"

	_ "modernc.org/sqlite"
)

type DB struct {
	sql    *sql.DB
	DbPath string
}

const schema = `
CREATE TABLE IF NOT EXISTS webui_meta (
	key   TEXT PRIMARY KEY,
	value TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS webui_config (
	key   TEXT PRIMARY KEY,
	value TEXT NOT NULL
);
`

func Init(dataDir string) (*DB, error) {
	if err := os.MkdirAll(dataDir, 0o755); err != nil {
		return nil, fmt.Errorf("create data dir: %w", err)
	}
	dbPath := filepath.Join(dataDir, "webui.db")
	sqlDB, err := sql.Open("sqlite", dbPath)
	if err != nil {
		return nil, fmt.Errorf("open sqlite: %w", err)
	}
	if _, err := sqlDB.Exec(schema); err != nil {
		return nil, fmt.Errorf("init schema: %w", err)
	}
	// ensure schema version
	_, _ = sqlDB.Exec(`INSERT OR IGNORE INTO webui_meta (key,value) VALUES ('schema_version','1')`)
	return &DB{sql: sqlDB, DbPath: dbPath}, nil
}

func (d *DB) Close() { _ = d.sql.Close() }

// ── Generic config key-value ──────────────────────────────────────────────────

func (d *DB) GetConfig(key string) (string, bool) {
	var val string
	err := d.sql.QueryRow(`SELECT value FROM webui_config WHERE key=?`, key).Scan(&val)
	if err != nil {
		return "", false
	}
	return val, true
}

func (d *DB) SetConfig(key, value string) error {
	_, err := d.sql.Exec(`INSERT OR REPLACE INTO webui_config (key,value) VALUES (?,?)`, key, value)
	return err
}

func (d *DB) DeleteConfig(key string) error {
	_, err := d.sql.Exec(`DELETE FROM webui_config WHERE key=?`, key)
	return err
}

func (d *DB) AllConfig() (map[string]string, error) {
	rows, err := d.sql.Query(`SELECT key,value FROM webui_config`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	m := map[string]string{}
	for rows.Next() {
		var k, v string
		if err := rows.Scan(&k, &v); err != nil {
			return nil, err
		}
		m[k] = v
	}
	return m, nil
}

// ── Pinned sessions (stored as JSON array in webui_config) ───────────────────

const pinnedKey = "__pinned_sessions__"

func (d *DB) GetPinnedSessions() ([]string, error) {
	val, ok := d.GetConfig(pinnedKey)
	if !ok {
		return nil, nil
	}
	var paths []string
	if err := json.Unmarshal([]byte(val), &paths); err != nil {
		return nil, err
	}
	return paths, nil
}

func (d *DB) SetSessionPinned(path string, pinned bool) error {
	paths, _ := d.GetPinnedSessions()
	set := map[string]struct{}{}
	for _, p := range paths {
		set[p] = struct{}{}
	}
	if pinned {
		set[path] = struct{}{}
	} else {
		delete(set, path)
	}
	newPaths := make([]string, 0, len(set))
	for p := range set {
		newPaths = append(newPaths, p)
	}
	b, err := json.Marshal(newPaths)
	if err != nil {
		return err
	}
	return d.SetConfig(pinnedKey, string(b))
}
