package db

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

	_ "modernc.org/sqlite"
)

func Open(path string) (*sql.DB, error) {
	if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
		return nil, err
	}

	database, err := sql.Open("sqlite", path)
	if err != nil {
		return nil, err
	}

	database.SetMaxOpenConns(1)
	database.SetConnMaxLifetime(30 * time.Minute)

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	if err := database.PingContext(ctx); err != nil {
		return nil, err
	}

	if err := migrate(ctx, database); err != nil {
		return nil, err
	}

	return database, nil
}

func migrate(ctx context.Context, database *sql.DB) error {
	statements := []string{
		`PRAGMA foreign_keys = ON;`,
		`CREATE TABLE IF NOT EXISTS organizations (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			name TEXT NOT NULL,
			theme TEXT NOT NULL DEFAULT 'emerald',
			theme_mode TEXT NOT NULL DEFAULT 'dark',
			auto_update_window_start TEXT NOT NULL DEFAULT '',
			auto_update_window_end TEXT NOT NULL DEFAULT '',
			auto_update_days TEXT NOT NULL DEFAULT '',
			created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
		);`,
		`CREATE TABLE IF NOT EXISTS users (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			organization_id INTEGER NOT NULL,
			name TEXT NOT NULL,
			email TEXT NOT NULL UNIQUE,
			password_hash TEXT NOT NULL,
			role TEXT NOT NULL,
			otp_secret TEXT NOT NULL,
			otp_enabled BOOLEAN NOT NULL DEFAULT 0,
			theme_mode TEXT NOT NULL DEFAULT '',
			created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
			FOREIGN KEY (organization_id) REFERENCES organizations(id)
		);`,
		`CREATE TABLE IF NOT EXISTS vm_groups (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			organization_id INTEGER NOT NULL,
			name TEXT NOT NULL,
			description TEXT NOT NULL DEFAULT '',
			color_token TEXT NOT NULL DEFAULT 'primary',
			icon TEXT NOT NULL DEFAULT 'ti ti-stack-2',
			created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
			FOREIGN KEY (organization_id) REFERENCES organizations(id)
		);`,
		`CREATE TABLE IF NOT EXISTS nodes (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			organization_id INTEGER NOT NULL,
			group_id INTEGER,
			tag TEXT NOT NULL DEFAULT '',
			name TEXT NOT NULL,
			distro TEXT NOT NULL,
			hostname TEXT NOT NULL,
			ip_address TEXT NOT NULL,
			mac_address TEXT NOT NULL DEFAULT '',
			ssh_port INTEGER NOT NULL DEFAULT 22,
			ssh_username TEXT NOT NULL DEFAULT '',
			ssh_password TEXT NOT NULL DEFAULT '',
			package_manager TEXT NOT NULL DEFAULT '',
			architecture TEXT NOT NULL DEFAULT '',
			host_model TEXT NOT NULL DEFAULT '',
			kernel_version TEXT NOT NULL DEFAULT '',
			cpu_model TEXT NOT NULL DEFAULT '',
			gpu_model TEXT NOT NULL DEFAULT '',
			default_shell TEXT NOT NULL DEFAULT '',
			package_count INTEGER NOT NULL DEFAULT 0,
			memory_total_mb INTEGER NOT NULL DEFAULT 0,
			disk_total_gb INTEGER NOT NULL DEFAULT 0,
			cpu_usage REAL NOT NULL DEFAULT 0,
			ram_usage REAL NOT NULL DEFAULT 0,
			disk_usage REAL NOT NULL DEFAULT 0,
			uptime_seconds INTEGER NOT NULL DEFAULT 0,
			last_seen_at DATETIME,
			updates_scan_enabled BOOLEAN NOT NULL DEFAULT 1,
			auto_updates_enabled BOOLEAN NOT NULL DEFAULT 0,
			updates_available INTEGER NOT NULL DEFAULT 0,
			updates_details TEXT NOT NULL DEFAULT '',
			auto_update_window_start TEXT NOT NULL DEFAULT '',
			auto_update_window_end TEXT NOT NULL DEFAULT '',
			auto_update_days TEXT NOT NULL DEFAULT '',
			updates_last_checked_at DATETIME,
			updates_last_error TEXT NOT NULL DEFAULT '',
			notes TEXT NOT NULL DEFAULT '',
			created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
			updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
			FOREIGN KEY (organization_id) REFERENCES organizations(id),
			FOREIGN KEY (group_id) REFERENCES vm_groups(id)
		);`,
		`CREATE TABLE IF NOT EXISTS automation_jobs (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			organization_id INTEGER NOT NULL,
			node_id INTEGER,
			group_id INTEGER,
			tag TEXT NOT NULL DEFAULT '',
			name TEXT NOT NULL,
			trigger_type TEXT NOT NULL,
			schedule TEXT NOT NULL DEFAULT '',
			command TEXT NOT NULL,
			enabled BOOLEAN NOT NULL DEFAULT 1,
			last_run_at DATETIME,
			next_run_at DATETIME,
			created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
			FOREIGN KEY (organization_id) REFERENCES organizations(id),
			FOREIGN KEY (node_id) REFERENCES nodes(id),
			FOREIGN KEY (group_id) REFERENCES vm_groups(id)
		);`,
		`CREATE TABLE IF NOT EXISTS command_runs (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			job_id INTEGER,
			node_id INTEGER NOT NULL,
			action TEXT NOT NULL,
			command_text TEXT NOT NULL DEFAULT '',
			status TEXT NOT NULL,
			output TEXT NOT NULL DEFAULT '',
			triggered_by INTEGER,
			started_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
			finished_at DATETIME,
			FOREIGN KEY (job_id) REFERENCES automation_jobs(id),
			FOREIGN KEY (node_id) REFERENCES nodes(id),
			FOREIGN KEY (triggered_by) REFERENCES users(id)
		);`,
		`CREATE TABLE IF NOT EXISTS uptime_monitors (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			organization_id INTEGER NOT NULL,
			node_id INTEGER NOT NULL UNIQUE,
			name TEXT NOT NULL,
			target TEXT NOT NULL,
			monitor_type TEXT NOT NULL DEFAULT 'ssh',
			interval_seconds INTEGER NOT NULL DEFAULT 60,
			enabled BOOLEAN NOT NULL DEFAULT 1,
			last_status TEXT NOT NULL DEFAULT 'unknown',
			last_latency_ms INTEGER NOT NULL DEFAULT 0,
			last_checked_at DATETIME,
			last_error TEXT NOT NULL DEFAULT '',
			up_since_at DATETIME,
			current_outage_started_at DATETIME,
			created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
			updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
			FOREIGN KEY (organization_id) REFERENCES organizations(id),
			FOREIGN KEY (node_id) REFERENCES nodes(id)
		);`,
		`CREATE TABLE IF NOT EXISTS uptime_checks (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			monitor_id INTEGER NOT NULL,
			status TEXT NOT NULL,
			latency_ms INTEGER NOT NULL DEFAULT 0,
			error_message TEXT NOT NULL DEFAULT '',
			checked_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
			FOREIGN KEY (monitor_id) REFERENCES uptime_monitors(id)
		);`,
		`CREATE TABLE IF NOT EXISTS uptime_incidents (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			monitor_id INTEGER NOT NULL,
			error_message TEXT NOT NULL DEFAULT '',
			started_at DATETIME NOT NULL,
			ended_at DATETIME,
			created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
			FOREIGN KEY (monitor_id) REFERENCES uptime_monitors(id)
		);`,
		`CREATE TABLE IF NOT EXISTS log_archives (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			organization_id INTEGER NOT NULL,
			archive_date TEXT NOT NULL,
			file_path TEXT NOT NULL,
			entry_count INTEGER NOT NULL DEFAULT 0,
			compressed_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
			UNIQUE (organization_id, archive_date),
			FOREIGN KEY (organization_id) REFERENCES organizations(id)
		);`,
	}

	for _, statement := range statements {
		if _, err := database.ExecContext(ctx, statement); err != nil {
			return fmt.Errorf("migrate: %w", err)
		}
	}

	alterStatements := []string{
		`ALTER TABLE organizations ADD COLUMN theme_mode TEXT NOT NULL DEFAULT 'dark';`,
		`ALTER TABLE organizations ADD COLUMN auto_update_window_start TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE organizations ADD COLUMN auto_update_window_end TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE organizations ADD COLUMN auto_update_days TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE users ADD COLUMN theme_mode TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE vm_groups ADD COLUMN icon TEXT NOT NULL DEFAULT 'ti ti-stack-2';`,
		`ALTER TABLE nodes ADD COLUMN tag TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE nodes ADD COLUMN package_manager TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE nodes ADD COLUMN architecture TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE nodes ADD COLUMN host_model TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE nodes ADD COLUMN kernel_version TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE nodes ADD COLUMN cpu_model TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE nodes ADD COLUMN gpu_model TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE nodes ADD COLUMN default_shell TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE nodes ADD COLUMN package_count INTEGER NOT NULL DEFAULT 0;`,
		`ALTER TABLE nodes ADD COLUMN memory_total_mb INTEGER NOT NULL DEFAULT 0;`,
		`ALTER TABLE nodes ADD COLUMN disk_total_gb INTEGER NOT NULL DEFAULT 0;`,
		`ALTER TABLE nodes ADD COLUMN updates_scan_enabled BOOLEAN NOT NULL DEFAULT 1;`,
		`ALTER TABLE nodes ADD COLUMN updates_available INTEGER NOT NULL DEFAULT 0;`,
		`ALTER TABLE nodes ADD COLUMN updates_details TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE nodes ADD COLUMN auto_update_window_start TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE nodes ADD COLUMN auto_update_window_end TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE nodes ADD COLUMN auto_update_days TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE nodes ADD COLUMN updates_last_checked_at DATETIME;`,
		`ALTER TABLE nodes ADD COLUMN updates_last_error TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE automation_jobs ADD COLUMN tag TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE command_runs ADD COLUMN command_text TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE uptime_monitors ADD COLUMN last_error TEXT NOT NULL DEFAULT '';`,
		`ALTER TABLE uptime_monitors ADD COLUMN up_since_at DATETIME;`,
	}

	for _, statement := range alterStatements {
		_, _ = database.ExecContext(ctx, statement)
	}

	return nil
}
