// Package database handles SQLite database operations
package database

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

	"github.com/google/uuid"
	"golang.org/x/crypto/bcrypt"
	_ "modernc.org/sqlite"
)

// DB wraps the database connection with business logic
type DB struct {
	*sql.DB
}

// Developer represents a developer account
type Developer struct {
	ID            string
	Email         string
	Name          string
	PasswordHash  string
	OAuthProvider string
	OAuthID       string
	AvatarURL     string
	Verified      bool
	CreatedAt     time.Time
	UpdatedAt     time.Time
}

// NewDB creates a new DB wrapper
func NewDB(db *sql.DB) *DB {
	return &DB{db}
}

// Open opens the SQLite database with WAL mode enabled
func Open(path string) (*sql.DB, error) {
	// Ensure directory exists
	dir := filepath.Dir(path)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return nil, fmt.Errorf("create database directory: %w", err)
	}

	// Open database with WAL mode and busy timeout
	dsn := fmt.Sprintf("%s?_pragma=journal_mode(WAL)&_pragma=busy_timeout(5000)&_pragma=foreign_keys(ON)", path)
	db, err := sql.Open("sqlite", dsn)
	if err != nil {
		return nil, fmt.Errorf("open database: %w", err)
	}

	// Test connection
	if err := db.Ping(); err != nil {
		db.Close()
		return nil, fmt.Errorf("ping database: %w", err)
	}

	// Set connection pool settings for SQLite
	db.SetMaxOpenConns(1) // SQLite single writer
	db.SetMaxIdleConns(1)

	return db, nil
}

// Migrate runs all database migrations
func Migrate(db *sql.DB) error {
	migrations := []string{
		migrationDevelopers,
		migrationAPIKeys,
		migrationApps,
		migrationAppVersions,
		migrationSigningKeys,
		migrationTelemetry,
		migrationAuditLogs,
		migrationIndexes,
	}

	for i, migration := range migrations {
		if _, err := db.Exec(migration); err != nil {
			return fmt.Errorf("migration %d: %w", i+1, err)
		}
	}

	return nil
}

const migrationDevelopers = `
CREATE TABLE IF NOT EXISTS developers (
    id TEXT PRIMARY KEY,
    email TEXT UNIQUE NOT NULL,
    name TEXT NOT NULL,
    password_hash TEXT,
    oauth_provider TEXT,
    oauth_id TEXT,
    verified INTEGER DEFAULT 0,
    created_at TEXT DEFAULT (datetime('now')),
    updated_at TEXT DEFAULT (datetime('now'))
);
`

const migrationAPIKeys = `
CREATE TABLE IF NOT EXISTS api_keys (
    id TEXT PRIMARY KEY,
    developer_id TEXT NOT NULL REFERENCES developers(id) ON DELETE CASCADE,
    name TEXT NOT NULL,
    key_hash TEXT NOT NULL,
    key_prefix TEXT NOT NULL,
    permissions TEXT DEFAULT '[]',
    last_used_at TEXT,
    expires_at TEXT,
    created_at TEXT DEFAULT (datetime('now'))
);
`

const migrationApps = `
CREATE TABLE IF NOT EXISTS apps (
    id TEXT PRIMARY KEY,
    developer_id TEXT NOT NULL REFERENCES developers(id) ON DELETE CASCADE,
    package_id TEXT UNIQUE NOT NULL,
    name TEXT NOT NULL,
    description TEXT,
    category TEXT,
    tags TEXT DEFAULT '[]',
    status TEXT DEFAULT 'draft',
    created_at TEXT DEFAULT (datetime('now')),
    updated_at TEXT DEFAULT (datetime('now'))
);
`

const migrationAppVersions = `
CREATE TABLE IF NOT EXISTS app_versions (
    id TEXT PRIMARY KEY,
    app_id TEXT NOT NULL REFERENCES apps(id) ON DELETE CASCADE,
    version_code INTEGER NOT NULL,
    version_name TEXT NOT NULL,
    package_url TEXT NOT NULL,
    package_size INTEGER NOT NULL,
    signature TEXT NOT NULL,
    permissions TEXT DEFAULT '[]',
    min_mosis_version TEXT,
    release_notes TEXT,
    status TEXT DEFAULT 'draft',
    review_notes TEXT,
    published_at TEXT,
    created_at TEXT DEFAULT (datetime('now')),
    UNIQUE(app_id, version_code)
);
`

const migrationSigningKeys = `
CREATE TABLE IF NOT EXISTS signing_keys (
    id TEXT PRIMARY KEY,
    developer_id TEXT NOT NULL REFERENCES developers(id) ON DELETE CASCADE,
    name TEXT NOT NULL,
    public_key TEXT NOT NULL,
    fingerprint TEXT NOT NULL,
    is_active INTEGER DEFAULT 1,
    created_at TEXT DEFAULT (datetime('now'))
);
`

const migrationTelemetry = `
CREATE TABLE IF NOT EXISTS telemetry_events (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    app_id TEXT NOT NULL,
    device_id TEXT NOT NULL,
    event_type TEXT NOT NULL,
    event_data TEXT,
    mosis_version TEXT,
    timestamp TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS crash_reports (
    id TEXT PRIMARY KEY,
    app_id TEXT NOT NULL,
    app_version TEXT NOT NULL,
    device_id TEXT NOT NULL,
    crash_type TEXT NOT NULL,
    message TEXT,
    stack_trace TEXT,
    context TEXT,
    mosis_version TEXT,
    timestamp TEXT NOT NULL,
    created_at TEXT DEFAULT (datetime('now'))
);

CREATE TABLE IF NOT EXISTS telemetry_daily (
    app_id TEXT NOT NULL,
    date TEXT NOT NULL,
    event_type TEXT NOT NULL,
    count INTEGER NOT NULL,
    unique_devices INTEGER NOT NULL,
    PRIMARY KEY (app_id, date, event_type)
);
`

const migrationAuditLogs = `
CREATE TABLE IF NOT EXISTS audit_logs (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    developer_id TEXT,
    action TEXT NOT NULL,
    resource_type TEXT,
    resource_id TEXT,
    details TEXT,
    ip_address TEXT,
    user_agent TEXT,
    created_at TEXT DEFAULT (datetime('now'))
);
`

const migrationIndexes = `
CREATE INDEX IF NOT EXISTS idx_developers_email ON developers(email);
CREATE INDEX IF NOT EXISTS idx_developers_oauth ON developers(oauth_provider, oauth_id);
CREATE INDEX IF NOT EXISTS idx_api_keys_developer ON api_keys(developer_id);
CREATE INDEX IF NOT EXISTS idx_api_keys_prefix ON api_keys(key_prefix);
CREATE INDEX IF NOT EXISTS idx_apps_developer ON apps(developer_id);
CREATE INDEX IF NOT EXISTS idx_apps_package ON apps(package_id);
CREATE INDEX IF NOT EXISTS idx_apps_status ON apps(status);
CREATE INDEX IF NOT EXISTS idx_versions_app ON app_versions(app_id);
CREATE INDEX IF NOT EXISTS idx_versions_status ON app_versions(status);
CREATE INDEX IF NOT EXISTS idx_signing_keys_developer ON signing_keys(developer_id);
CREATE INDEX IF NOT EXISTS idx_signing_keys_fingerprint ON signing_keys(fingerprint);
CREATE INDEX IF NOT EXISTS idx_telemetry_app ON telemetry_events(app_id, timestamp);
CREATE INDEX IF NOT EXISTS idx_crashes_app ON crash_reports(app_id, timestamp);
CREATE INDEX IF NOT EXISTS idx_audit_developer ON audit_logs(developer_id);
CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_logs(created_at);
`

// FindOrCreateDeveloper finds an existing developer by email or creates a new one
func (db *DB) FindOrCreateDeveloper(ctx context.Context, dev *Developer) (*Developer, error) {
	// First try to find by email
	existing, err := db.GetDeveloperByEmail(ctx, dev.Email)
	if err == nil {
		// Update OAuth info if changed
		if dev.OAuthProvider != "" && (existing.OAuthProvider != dev.OAuthProvider || existing.OAuthID != dev.OAuthID) {
			_, err := db.ExecContext(ctx, `
				UPDATE developers SET oauth_provider = ?, oauth_id = ?, updated_at = datetime('now')
				WHERE id = ?
			`, dev.OAuthProvider, dev.OAuthID, existing.ID)
			if err != nil {
				return nil, fmt.Errorf("update oauth: %w", err)
			}
			existing.OAuthProvider = dev.OAuthProvider
			existing.OAuthID = dev.OAuthID
		}
		return existing, nil
	}

	// Create new developer
	dev.ID = uuid.New().String()
	_, err = db.ExecContext(ctx, `
		INSERT INTO developers (id, email, name, oauth_provider, oauth_id, verified)
		VALUES (?, ?, ?, ?, ?, 1)
	`, dev.ID, dev.Email, dev.Name, dev.OAuthProvider, dev.OAuthID)
	if err != nil {
		return nil, fmt.Errorf("create developer: %w", err)
	}

	dev.Verified = true
	dev.CreatedAt = time.Now()
	dev.UpdatedAt = dev.CreatedAt
	return dev, nil
}

// GetDeveloper retrieves a developer by ID
func (db *DB) GetDeveloper(ctx context.Context, id string) (*Developer, error) {
	row := db.QueryRowContext(ctx, `
		SELECT id, email, name, password_hash, oauth_provider, oauth_id, verified, created_at, updated_at
		FROM developers WHERE id = ?
	`, id)

	return scanDeveloper(row)
}

// GetDeveloperByEmail retrieves a developer by email
func (db *DB) GetDeveloperByEmail(ctx context.Context, email string) (*Developer, error) {
	row := db.QueryRowContext(ctx, `
		SELECT id, email, name, password_hash, oauth_provider, oauth_id, verified, created_at, updated_at
		FROM developers WHERE email = ?
	`, email)

	return scanDeveloper(row)
}

func scanDeveloper(row *sql.Row) (*Developer, error) {
	var dev Developer
	var passwordHash, oauthProvider, oauthID sql.NullString
	var createdAt, updatedAt string

	err := row.Scan(&dev.ID, &dev.Email, &dev.Name, &passwordHash, &oauthProvider, &oauthID, &dev.Verified, &createdAt, &updatedAt)
	if err != nil {
		return nil, err
	}

	dev.PasswordHash = passwordHash.String
	dev.OAuthProvider = oauthProvider.String
	dev.OAuthID = oauthID.String
	dev.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", createdAt)
	dev.UpdatedAt, _ = time.Parse("2006-01-02 15:04:05", updatedAt)

	return &dev, nil
}

// ValidateAPIKey validates an API key and returns the associated developer
func (db *DB) ValidateAPIKey(ctx context.Context, key string) (*Developer, error) {
	// Extract prefix (first 15 chars: mk_live_xxxxxxx)
	if len(key) < 15 {
		return nil, fmt.Errorf("invalid key format")
	}
	prefix := key[:15]

	// Find key by prefix
	row := db.QueryRowContext(ctx, `
		SELECT k.key_hash, k.developer_id, k.expires_at
		FROM api_keys k
		WHERE k.key_prefix = ?
	`, prefix)

	var keyHash, developerID string
	var expiresAt sql.NullString
	if err := row.Scan(&keyHash, &developerID, &expiresAt); err != nil {
		return nil, fmt.Errorf("key not found")
	}

	// Check expiration
	if expiresAt.Valid {
		expiry, err := time.Parse("2006-01-02 15:04:05", expiresAt.String)
		if err == nil && time.Now().After(expiry) {
			return nil, fmt.Errorf("key expired")
		}
	}

	// Verify key hash
	if err := bcrypt.CompareHashAndPassword([]byte(keyHash), []byte(key)); err != nil {
		return nil, fmt.Errorf("invalid key")
	}

	// Update last used
	db.ExecContext(ctx, `UPDATE api_keys SET last_used_at = datetime('now') WHERE key_prefix = ?`, prefix)

	// Get developer
	return db.GetDeveloper(ctx, developerID)
}

// LogAudit logs an audit event
func (db *DB) LogAudit(ctx context.Context, developerID, action, ipAddress, userAgent string, success bool, failureReason string) {
	details := ""
	if !success {
		details = fmt.Sprintf(`{"success":false,"reason":"%s"}`, failureReason)
	} else {
		details = `{"success":true}`
	}

	db.ExecContext(ctx, `
		INSERT INTO audit_logs (developer_id, action, details, ip_address, user_agent)
		VALUES (?, ?, ?, ?, ?)
	`, developerID, action, details, ipAddress, userAgent)
}

// App represents an app in the database
type App struct {
	ID          string    `json:"id"`
	DeveloperID string    `json:"developer_id"`
	PackageID   string    `json:"package_id"`
	Name        string    `json:"name"`
	Description string    `json:"description,omitempty"`
	Category    string    `json:"category,omitempty"`
	Tags        []string  `json:"tags"`
	Status      string    `json:"status"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
}

// AppVersion represents an app version in the database
type AppVersion struct {
	ID              string     `json:"id"`
	AppID           string     `json:"app_id"`
	VersionCode     int        `json:"version_code"`
	VersionName     string     `json:"version_name"`
	PackageURL      string     `json:"package_url,omitempty"`
	PackageSize     int64      `json:"package_size,omitempty"`
	Signature       string     `json:"signature,omitempty"`
	Permissions     []string   `json:"permissions"`
	MinMosisVersion string     `json:"min_mosis_version,omitempty"`
	ReleaseNotes    string     `json:"release_notes,omitempty"`
	Status          string     `json:"status"`
	ReviewNotes     string     `json:"review_notes,omitempty"`
	PublishedAt     *time.Time `json:"published_at,omitempty"`
	CreatedAt       time.Time  `json:"created_at"`
}

// CreateApp creates a new app
func (db *DB) CreateApp(ctx context.Context, app *App) (*App, error) {
	app.ID = uuid.New().String()
	app.Tags = []string{}
	app.CreatedAt = time.Now()
	app.UpdatedAt = app.CreatedAt

	_, err := db.ExecContext(ctx, `
		INSERT INTO apps (id, developer_id, package_id, name, description, category, tags, status, created_at, updated_at)
		VALUES (?, ?, ?, ?, ?, ?, '[]', ?, datetime('now'), datetime('now'))
	`, app.ID, app.DeveloperID, app.PackageID, app.Name, app.Description, app.Category, app.Status)
	if err != nil {
		return nil, err
	}

	return app, nil
}

// GetApp retrieves an app by ID
func (db *DB) GetApp(ctx context.Context, id string) (*App, error) {
	row := db.QueryRowContext(ctx, `
		SELECT id, developer_id, package_id, name, description, category, tags, status, created_at, updated_at
		FROM apps WHERE id = ?
	`, id)

	return scanApp(row)
}

// GetAppByPackageID retrieves an app by package ID
func (db *DB) GetAppByPackageID(ctx context.Context, packageID string) (*App, error) {
	row := db.QueryRowContext(ctx, `
		SELECT id, developer_id, package_id, name, description, category, tags, status, created_at, updated_at
		FROM apps WHERE package_id = ?
	`, packageID)

	return scanApp(row)
}

func scanApp(row *sql.Row) (*App, error) {
	var app App
	var desc, cat, tagsJSON sql.NullString
	var createdAt, updatedAt string

	err := row.Scan(&app.ID, &app.DeveloperID, &app.PackageID, &app.Name, &desc, &cat, &tagsJSON, &app.Status, &createdAt, &updatedAt)
	if err != nil {
		return nil, err
	}

	app.Description = desc.String
	app.Category = cat.String
	app.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", createdAt)
	app.UpdatedAt, _ = time.Parse("2006-01-02 15:04:05", updatedAt)

	// Parse tags JSON
	app.Tags = []string{}
	if tagsJSON.Valid && tagsJSON.String != "" {
		json.Unmarshal([]byte(tagsJSON.String), &app.Tags)
	}

	return &app, nil
}

// ListApps lists apps for a developer
func (db *DB) ListApps(ctx context.Context, developerID, status string, page, limit int) ([]*App, int, error) {
	offset := (page - 1) * limit

	// Build query
	query := "SELECT id, developer_id, package_id, name, description, category, tags, status, created_at, updated_at FROM apps WHERE developer_id = ?"
	countQuery := "SELECT COUNT(*) FROM apps WHERE developer_id = ?"
	args := []interface{}{developerID}
	countArgs := []interface{}{developerID}

	if status != "" {
		query += " AND status = ?"
		countQuery += " AND status = ?"
		args = append(args, status)
		countArgs = append(countArgs, status)
	}

	query += " ORDER BY updated_at DESC LIMIT ? OFFSET ?"
	args = append(args, limit, offset)

	// Get total count
	var total int
	db.QueryRowContext(ctx, countQuery, countArgs...).Scan(&total)

	// Get apps
	rows, err := db.QueryContext(ctx, query, args...)
	if err != nil {
		return nil, 0, err
	}
	defer rows.Close()

	var apps []*App
	for rows.Next() {
		var app App
		var desc, cat, tagsJSON sql.NullString
		var createdAt, updatedAt string

		err := rows.Scan(&app.ID, &app.DeveloperID, &app.PackageID, &app.Name, &desc, &cat, &tagsJSON, &app.Status, &createdAt, &updatedAt)
		if err != nil {
			continue
		}

		app.Description = desc.String
		app.Category = cat.String
		app.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", createdAt)
		app.UpdatedAt, _ = time.Parse("2006-01-02 15:04:05", updatedAt)
		app.Tags = []string{}
		if tagsJSON.Valid && tagsJSON.String != "" {
			json.Unmarshal([]byte(tagsJSON.String), &app.Tags)
		}

		apps = append(apps, &app)
	}

	return apps, total, nil
}

// UpdateApp updates an app
func (db *DB) UpdateApp(ctx context.Context, app *App) error {
	tagsJSON, _ := json.Marshal(app.Tags)
	_, err := db.ExecContext(ctx, `
		UPDATE apps SET name = ?, description = ?, category = ?, tags = ?, updated_at = datetime('now')
		WHERE id = ?
	`, app.Name, app.Description, app.Category, string(tagsJSON), app.ID)
	return err
}

// UpdateAppStatus updates an app's status
func (db *DB) UpdateAppStatus(ctx context.Context, id, status string) error {
	_, err := db.ExecContext(ctx, `
		UPDATE apps SET status = ?, updated_at = datetime('now') WHERE id = ?
	`, status, id)
	return err
}

// DeleteApp deletes an app and its versions
func (db *DB) DeleteApp(ctx context.Context, id string) error {
	_, err := db.ExecContext(ctx, `DELETE FROM apps WHERE id = ?`, id)
	return err
}

// AppHasPublishedVersions checks if an app has any published versions
func (db *DB) AppHasPublishedVersions(ctx context.Context, appID string) (bool, error) {
	var count int
	err := db.QueryRowContext(ctx, `
		SELECT COUNT(*) FROM app_versions WHERE app_id = ? AND status = 'published'
	`, appID).Scan(&count)
	return count > 0, err
}

// CreateVersion creates a new app version
func (db *DB) CreateVersion(ctx context.Context, version *AppVersion) (*AppVersion, error) {
	version.ID = uuid.New().String()
	version.Permissions = []string{}
	version.CreatedAt = time.Now()

	_, err := db.ExecContext(ctx, `
		INSERT INTO app_versions (id, app_id, version_code, version_name, package_url, package_size, signature, permissions, min_mosis_version, release_notes, status, created_at)
		VALUES (?, ?, ?, ?, '', 0, '', '[]', '', ?, 'draft', datetime('now'))
	`, version.ID, version.AppID, version.VersionCode, version.VersionName, version.ReleaseNotes)
	if err != nil {
		return nil, err
	}

	return version, nil
}

// GetVersion retrieves a version by ID
func (db *DB) GetVersion(ctx context.Context, id string) (*AppVersion, error) {
	row := db.QueryRowContext(ctx, `
		SELECT id, app_id, version_code, version_name, package_url, package_size, signature, permissions, min_mosis_version, release_notes, status, review_notes, published_at, created_at
		FROM app_versions WHERE id = ?
	`, id)

	return scanVersion(row)
}

func scanVersion(row *sql.Row) (*AppVersion, error) {
	var v AppVersion
	var packageURL, signature, permsJSON, minVersion, releaseNotes, reviewNotes sql.NullString
	var publishedAt, createdAt sql.NullString
	var packageSize sql.NullInt64

	err := row.Scan(&v.ID, &v.AppID, &v.VersionCode, &v.VersionName, &packageURL, &packageSize, &signature, &permsJSON, &minVersion, &releaseNotes, &v.Status, &reviewNotes, &publishedAt, &createdAt)
	if err != nil {
		return nil, err
	}

	v.PackageURL = packageURL.String
	v.PackageSize = packageSize.Int64
	v.Signature = signature.String
	v.MinMosisVersion = minVersion.String
	v.ReleaseNotes = releaseNotes.String
	v.ReviewNotes = reviewNotes.String

	v.Permissions = []string{}
	if permsJSON.Valid && permsJSON.String != "" {
		json.Unmarshal([]byte(permsJSON.String), &v.Permissions)
	}

	if createdAt.Valid {
		v.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", createdAt.String)
	}
	if publishedAt.Valid {
		t, _ := time.Parse("2006-01-02 15:04:05", publishedAt.String)
		v.PublishedAt = &t
	}

	return &v, nil
}

// ListVersions lists versions for an app
func (db *DB) ListVersions(ctx context.Context, appID, status string, page, limit int) ([]*AppVersion, int, error) {
	offset := (page - 1) * limit

	query := "SELECT id, app_id, version_code, version_name, package_url, package_size, signature, permissions, min_mosis_version, release_notes, status, review_notes, published_at, created_at FROM app_versions WHERE app_id = ?"
	countQuery := "SELECT COUNT(*) FROM app_versions WHERE app_id = ?"
	args := []interface{}{appID}
	countArgs := []interface{}{appID}

	if status != "" {
		query += " AND status = ?"
		countQuery += " AND status = ?"
		args = append(args, status)
		countArgs = append(countArgs, status)
	}

	query += " ORDER BY version_code DESC LIMIT ? OFFSET ?"
	args = append(args, limit, offset)

	var total int
	db.QueryRowContext(ctx, countQuery, countArgs...).Scan(&total)

	rows, err := db.QueryContext(ctx, query, args...)
	if err != nil {
		return nil, 0, err
	}
	defer rows.Close()

	var versions []*AppVersion
	for rows.Next() {
		var v AppVersion
		var packageURL, signature, permsJSON, minVersion, releaseNotes, reviewNotes sql.NullString
		var publishedAt, createdAt sql.NullString
		var packageSize sql.NullInt64

		err := rows.Scan(&v.ID, &v.AppID, &v.VersionCode, &v.VersionName, &packageURL, &packageSize, &signature, &permsJSON, &minVersion, &releaseNotes, &v.Status, &reviewNotes, &publishedAt, &createdAt)
		if err != nil {
			continue
		}

		v.PackageURL = packageURL.String
		v.PackageSize = packageSize.Int64
		v.Signature = signature.String
		v.MinMosisVersion = minVersion.String
		v.ReleaseNotes = releaseNotes.String
		v.ReviewNotes = reviewNotes.String

		v.Permissions = []string{}
		if permsJSON.Valid && permsJSON.String != "" {
			json.Unmarshal([]byte(permsJSON.String), &v.Permissions)
		}

		if createdAt.Valid {
			v.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", createdAt.String)
		}
		if publishedAt.Valid {
			t, _ := time.Parse("2006-01-02 15:04:05", publishedAt.String)
			v.PublishedAt = &t
		}

		versions = append(versions, &v)
	}

	return versions, total, nil
}

// VersionCodeExists checks if a version code exists for an app
func (db *DB) VersionCodeExists(ctx context.Context, appID string, code int) (bool, error) {
	var count int
	err := db.QueryRowContext(ctx, `
		SELECT COUNT(*) FROM app_versions WHERE app_id = ? AND version_code = ?
	`, appID, code).Scan(&count)
	return count > 0, err
}

// UpdateVersionStatus updates a version's status
func (db *DB) UpdateVersionStatus(ctx context.Context, id, status string) error {
	_, err := db.ExecContext(ctx, `
		UPDATE app_versions SET status = ? WHERE id = ?
	`, status, id)
	return err
}

// PublishVersion publishes a version
func (db *DB) PublishVersion(ctx context.Context, id string) error {
	_, err := db.ExecContext(ctx, `
		UPDATE app_versions SET status = 'published', published_at = datetime('now') WHERE id = ?
	`, id)
	return err
}

// UpdateVersionPackage updates the package_url and package_size for a version
func (db *DB) UpdateVersionPackage(ctx context.Context, id, packageURL string, packageSize int64) error {
	_, err := db.ExecContext(ctx, `
		UPDATE app_versions SET package_url = ?, package_size = ? WHERE id = ?
	`, packageURL, packageSize, id)
	return err
}

// PublicApp represents a published app for the store
type PublicApp struct {
	PackageID     string    `json:"package_id"`
	Name          string    `json:"name"`
	Description   string    `json:"description,omitempty"`
	Category      string    `json:"category,omitempty"`
	Tags          []string  `json:"tags"`
	AuthorName    string    `json:"author_name"`
	LatestVersion string    `json:"latest_version"`
	DownloadCount int64     `json:"download_count"`
	CreatedAt     time.Time `json:"created_at"`
	UpdatedAt     time.Time `json:"updated_at"`
}

// ListPublishedApps lists published apps for the store
func (db *DB) ListPublishedApps(ctx context.Context, query, category, sort string, page, limit int) ([]*PublicApp, int, error) {
	offset := (page - 1) * limit

	// Base query
	baseQuery := `
		SELECT a.package_id, a.name, a.description, a.category, a.tags, d.name as author_name,
		       COALESCE(v.version_name, '') as latest_version, 0 as download_count,
		       a.created_at, a.updated_at
		FROM apps a
		JOIN developers d ON d.id = a.developer_id
		LEFT JOIN app_versions v ON v.app_id = a.id AND v.status = 'published'
		WHERE a.status = 'published'
	`
	countQuery := `SELECT COUNT(*) FROM apps WHERE status = 'published'`
	var args []interface{}
	var countArgs []interface{}

	// Search filter
	if query != "" {
		baseQuery += " AND (a.name LIKE ? OR a.description LIKE ? OR a.package_id LIKE ?)"
		countQuery += " AND (name LIKE ? OR description LIKE ? OR package_id LIKE ?)"
		searchTerm := "%" + query + "%"
		args = append(args, searchTerm, searchTerm, searchTerm)
		countArgs = append(countArgs, searchTerm, searchTerm, searchTerm)
	}

	// Category filter
	if category != "" {
		baseQuery += " AND a.category = ?"
		countQuery += " AND category = ?"
		args = append(args, category)
		countArgs = append(countArgs, category)
	}

	// Group by to handle multiple versions (pick latest)
	baseQuery += " GROUP BY a.id"

	// Sorting
	switch sort {
	case "name":
		baseQuery += " ORDER BY a.name ASC"
	case "recent":
		baseQuery += " ORDER BY a.updated_at DESC"
	default: // popular
		baseQuery += " ORDER BY a.updated_at DESC" // TODO: implement download count sorting
	}

	baseQuery += " LIMIT ? OFFSET ?"
	args = append(args, limit, offset)

	// Get total count
	var total int
	db.QueryRowContext(ctx, countQuery, countArgs...).Scan(&total)

	// Get apps
	rows, err := db.QueryContext(ctx, baseQuery, args...)
	if err != nil {
		return nil, 0, err
	}
	defer rows.Close()

	var apps []*PublicApp
	for rows.Next() {
		var app PublicApp
		var desc, cat, tagsJSON, latestVersion sql.NullString
		var createdAt, updatedAt string

		err := rows.Scan(&app.PackageID, &app.Name, &desc, &cat, &tagsJSON, &app.AuthorName,
			&latestVersion, &app.DownloadCount, &createdAt, &updatedAt)
		if err != nil {
			continue
		}

		app.Description = desc.String
		app.Category = cat.String
		app.LatestVersion = latestVersion.String
		app.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", createdAt)
		app.UpdatedAt, _ = time.Parse("2006-01-02 15:04:05", updatedAt)

		app.Tags = []string{}
		if tagsJSON.Valid && tagsJSON.String != "" {
			json.Unmarshal([]byte(tagsJSON.String), &app.Tags)
		}

		apps = append(apps, &app)
	}

	return apps, total, nil
}

// GetPublishedApp retrieves a published app by package ID for the store
func (db *DB) GetPublishedApp(ctx context.Context, packageID string) (*PublicApp, error) {
	row := db.QueryRowContext(ctx, `
		SELECT a.package_id, a.name, a.description, a.category, a.tags, d.name as author_name,
		       COALESCE(v.version_name, '') as latest_version, 0 as download_count,
		       a.created_at, a.updated_at
		FROM apps a
		JOIN developers d ON d.id = a.developer_id
		LEFT JOIN (
			SELECT app_id, version_name FROM app_versions
			WHERE status = 'published'
			ORDER BY version_code DESC LIMIT 1
		) v ON v.app_id = a.id
		WHERE a.package_id = ? AND a.status = 'published'
	`, packageID)

	var app PublicApp
	var desc, cat, tagsJSON, latestVersion sql.NullString
	var createdAt, updatedAt string

	err := row.Scan(&app.PackageID, &app.Name, &desc, &cat, &tagsJSON, &app.AuthorName,
		&latestVersion, &app.DownloadCount, &createdAt, &updatedAt)
	if err != nil {
		return nil, err
	}

	app.Description = desc.String
	app.Category = cat.String
	app.LatestVersion = latestVersion.String
	app.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", createdAt)
	app.UpdatedAt, _ = time.Parse("2006-01-02 15:04:05", updatedAt)

	app.Tags = []string{}
	if tagsJSON.Valid && tagsJSON.String != "" {
		json.Unmarshal([]byte(tagsJSON.String), &app.Tags)
	}

	return &app, nil
}

// GetLatestPublishedVersion retrieves the latest published version for an app by package ID
func (db *DB) GetLatestPublishedVersion(ctx context.Context, packageID string) (*AppVersion, error) {
	row := db.QueryRowContext(ctx, `
		SELECT v.id, v.app_id, v.version_code, v.version_name, v.package_url, v.package_size, v.signature,
		       v.permissions, v.min_mosis_version, v.release_notes, v.status, v.review_notes, v.published_at, v.created_at
		FROM app_versions v
		JOIN apps a ON a.id = v.app_id
		WHERE a.package_id = ? AND v.status = 'published'
		ORDER BY v.version_code DESC
		LIMIT 1
	`, packageID)

	return scanVersion(row)
}

// GetPublishedVersionByCode retrieves a specific published version by package ID and version code
func (db *DB) GetPublishedVersionByCode(ctx context.Context, packageID string, versionCode int) (*AppVersion, error) {
	row := db.QueryRowContext(ctx, `
		SELECT v.id, v.app_id, v.version_code, v.version_name, v.package_url, v.package_size, v.signature,
		       v.permissions, v.min_mosis_version, v.release_notes, v.status, v.review_notes, v.published_at, v.created_at
		FROM app_versions v
		JOIN apps a ON a.id = v.app_id
		WHERE a.package_id = ? AND v.version_code = ? AND v.status = 'published'
	`, packageID, versionCode)

	return scanVersion(row)
}

// VersionWithApp combines version data with its parent app data for review display
type VersionWithApp struct {
	Version       *AppVersion `json:"version"`
	App           *App        `json:"app"`
	DeveloperName string      `json:"developer_name"`
	DeveloperEmail string     `json:"developer_email"`
}

// GetVersionsInReview returns versions pending review with pagination
func (db *DB) GetVersionsInReview(ctx context.Context, limit, offset int) ([]VersionWithApp, int, error) {
	// Get total count
	var total int
	err := db.QueryRowContext(ctx, `
		SELECT COUNT(*) FROM app_versions WHERE status = 'in_review'
	`).Scan(&total)
	if err != nil {
		return nil, 0, err
	}

	// Query versions with app and developer info
	rows, err := db.QueryContext(ctx, `
		SELECT v.id, v.app_id, v.version_code, v.version_name, v.package_url, v.package_size, v.signature,
		       v.permissions, v.min_mosis_version, v.release_notes, v.status, v.review_notes, v.published_at, v.created_at,
		       a.id, a.developer_id, a.package_id, a.name, a.description, a.category, a.tags, a.status, a.created_at, a.updated_at,
		       d.name, d.email
		FROM app_versions v
		JOIN apps a ON a.id = v.app_id
		JOIN developers d ON d.id = a.developer_id
		WHERE v.status = 'in_review'
		ORDER BY v.created_at ASC
		LIMIT ? OFFSET ?
	`, limit, offset)
	if err != nil {
		return nil, 0, err
	}
	defer rows.Close()

	var results []VersionWithApp
	for rows.Next() {
		var vwa VersionWithApp
		var v AppVersion
		var app App

		var vPackageURL, vSignature, vPermsJSON, vMinVersion, vReleaseNotes, vReviewNotes sql.NullString
		var vPublishedAt, vCreatedAt sql.NullString
		var vPackageSize sql.NullInt64
		var aDesc, aCat, aTagsJSON sql.NullString
		var aCreatedAt, aUpdatedAt string

		err := rows.Scan(
			&v.ID, &v.AppID, &v.VersionCode, &v.VersionName, &vPackageURL, &vPackageSize, &vSignature,
			&vPermsJSON, &vMinVersion, &vReleaseNotes, &v.Status, &vReviewNotes, &vPublishedAt, &vCreatedAt,
			&app.ID, &app.DeveloperID, &app.PackageID, &app.Name, &aDesc, &aCat, &aTagsJSON, &app.Status, &aCreatedAt, &aUpdatedAt,
			&vwa.DeveloperName, &vwa.DeveloperEmail,
		)
		if err != nil {
			continue
		}

		// Populate version
		v.PackageURL = vPackageURL.String
		v.PackageSize = vPackageSize.Int64
		v.Signature = vSignature.String
		v.MinMosisVersion = vMinVersion.String
		v.ReleaseNotes = vReleaseNotes.String
		v.ReviewNotes = vReviewNotes.String
		v.Permissions = []string{}
		if vPermsJSON.Valid && vPermsJSON.String != "" {
			json.Unmarshal([]byte(vPermsJSON.String), &v.Permissions)
		}
		if vCreatedAt.Valid {
			v.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", vCreatedAt.String)
		}
		if vPublishedAt.Valid {
			t, _ := time.Parse("2006-01-02 15:04:05", vPublishedAt.String)
			v.PublishedAt = &t
		}

		// Populate app
		app.Description = aDesc.String
		app.Category = aCat.String
		app.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", aCreatedAt)
		app.UpdatedAt, _ = time.Parse("2006-01-02 15:04:05", aUpdatedAt)
		app.Tags = []string{}
		if aTagsJSON.Valid && aTagsJSON.String != "" {
			json.Unmarshal([]byte(aTagsJSON.String), &app.Tags)
		}

		vwa.Version = &v
		vwa.App = &app
		results = append(results, vwa)
	}

	return results, total, nil
}

// GetVersionWithApp retrieves a version with its app and developer info
func (db *DB) GetVersionWithApp(ctx context.Context, versionID string) (*VersionWithApp, error) {
	row := db.QueryRowContext(ctx, `
		SELECT v.id, v.app_id, v.version_code, v.version_name, v.package_url, v.package_size, v.signature,
		       v.permissions, v.min_mosis_version, v.release_notes, v.status, v.review_notes, v.published_at, v.created_at,
		       a.id, a.developer_id, a.package_id, a.name, a.description, a.category, a.tags, a.status, a.created_at, a.updated_at,
		       d.name, d.email
		FROM app_versions v
		JOIN apps a ON a.id = v.app_id
		JOIN developers d ON d.id = a.developer_id
		WHERE v.id = ?
	`, versionID)

	var vwa VersionWithApp
	var v AppVersion
	var app App

	var vPackageURL, vSignature, vPermsJSON, vMinVersion, vReleaseNotes, vReviewNotes sql.NullString
	var vPublishedAt, vCreatedAt sql.NullString
	var vPackageSize sql.NullInt64
	var aDesc, aCat, aTagsJSON sql.NullString
	var aCreatedAt, aUpdatedAt string

	err := row.Scan(
		&v.ID, &v.AppID, &v.VersionCode, &v.VersionName, &vPackageURL, &vPackageSize, &vSignature,
		&vPermsJSON, &vMinVersion, &vReleaseNotes, &v.Status, &vReviewNotes, &vPublishedAt, &vCreatedAt,
		&app.ID, &app.DeveloperID, &app.PackageID, &app.Name, &aDesc, &aCat, &aTagsJSON, &app.Status, &aCreatedAt, &aUpdatedAt,
		&vwa.DeveloperName, &vwa.DeveloperEmail,
	)
	if err != nil {
		return nil, err
	}

	// Populate version
	v.PackageURL = vPackageURL.String
	v.PackageSize = vPackageSize.Int64
	v.Signature = vSignature.String
	v.MinMosisVersion = vMinVersion.String
	v.ReleaseNotes = vReleaseNotes.String
	v.ReviewNotes = vReviewNotes.String
	v.Permissions = []string{}
	if vPermsJSON.Valid && vPermsJSON.String != "" {
		json.Unmarshal([]byte(vPermsJSON.String), &v.Permissions)
	}
	if vCreatedAt.Valid {
		v.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", vCreatedAt.String)
	}
	if vPublishedAt.Valid {
		t, _ := time.Parse("2006-01-02 15:04:05", vPublishedAt.String)
		v.PublishedAt = &t
	}

	// Populate app
	app.Description = aDesc.String
	app.Category = aCat.String
	app.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", aCreatedAt)
	app.UpdatedAt, _ = time.Parse("2006-01-02 15:04:05", aUpdatedAt)
	app.Tags = []string{}
	if aTagsJSON.Valid && aTagsJSON.String != "" {
		json.Unmarshal([]byte(aTagsJSON.String), &app.Tags)
	}

	vwa.Version = &v
	vwa.App = &app
	return &vwa, nil
}

// ApproveVersion approves a version and optionally publishes it
func (db *DB) ApproveVersion(ctx context.Context, versionID, reviewerNotes string) error {
	_, err := db.ExecContext(ctx, `
		UPDATE app_versions
		SET status = 'published', review_notes = ?, published_at = datetime('now')
		WHERE id = ?
	`, reviewerNotes, versionID)
	if err != nil {
		return err
	}

	// Also update the app status to published
	_, err = db.ExecContext(ctx, `
		UPDATE apps SET status = 'published', updated_at = datetime('now')
		WHERE id = (SELECT app_id FROM app_versions WHERE id = ?)
	`, versionID)
	return err
}

// RejectVersion rejects a version with feedback
func (db *DB) RejectVersion(ctx context.Context, versionID, reason, message string) error {
	notes := reason
	if message != "" {
		notes = reason + ": " + message
	}
	_, err := db.ExecContext(ctx, `
		UPDATE app_versions SET status = 'rejected', review_notes = ? WHERE id = ?
	`, notes, versionID)
	return err
}

// GetReviewStats returns statistics about the review queue
func (db *DB) GetReviewStats(ctx context.Context) (pending, approved, rejected int, err error) {
	err = db.QueryRowContext(ctx, `
		SELECT
			COALESCE(SUM(CASE WHEN status = 'in_review' THEN 1 ELSE 0 END), 0),
			COALESCE(SUM(CASE WHEN status = 'published' THEN 1 ELSE 0 END), 0),
			COALESCE(SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END), 0)
		FROM app_versions
	`).Scan(&pending, &approved, &rejected)
	return
}
