package db

import (
	"context"
	"database/sql"
	"log"

	"github.com/example/storage-appliance/internal/auth"
)

// MigrateAndSeed performs a very small migration set and seeds an admin user
func MigrateAndSeed(ctx context.Context, db *sql.DB) error {
	tx, err := db.Begin()
	if err != nil {
		return err
	}
	defer tx.Rollback()

	stmts := []string{
		`CREATE TABLE IF NOT EXISTS users (id TEXT PRIMARY KEY, username TEXT NOT NULL UNIQUE, password_hash TEXT, role TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP);`,
		`CREATE TABLE IF NOT EXISTS pools (name TEXT PRIMARY KEY, guid TEXT, health TEXT, capacity TEXT);`,
		`CREATE TABLE IF NOT EXISTS jobs (id TEXT PRIMARY KEY, type TEXT, status TEXT, progress INTEGER DEFAULT 0, owner TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME);`,
		`CREATE TABLE IF NOT EXISTS shares (id TEXT PRIMARY KEY, name TEXT, path TEXT, type TEXT, options TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP);`,
		`CREATE TABLE IF NOT EXISTS object_storage (id TEXT PRIMARY KEY, name TEXT, access_key TEXT, secret_key TEXT, data_path TEXT, port INTEGER, tls INTEGER DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP);`,
		`CREATE TABLE IF NOT EXISTS buckets (id TEXT PRIMARY KEY, name TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP);`,
		`CREATE TABLE IF NOT EXISTS iscsi_targets (id TEXT PRIMARY KEY, iqn TEXT NOT NULL UNIQUE, name TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP);`,
		`CREATE TABLE IF NOT EXISTS iscsi_portals (id TEXT PRIMARY KEY, target_id TEXT NOT NULL, address TEXT NOT NULL, port INTEGER DEFAULT 3260, created_at DATETIME DEFAULT CURRENT_TIMESTAMP);`,
		`CREATE TABLE IF NOT EXISTS iscsi_initiators (id TEXT PRIMARY KEY, target_id TEXT NOT NULL, initiator_iqn TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP);`,
		`CREATE TABLE IF NOT EXISTS iscsi_luns (id TEXT PRIMARY KEY, target_id TEXT NOT NULL, lun_id INTEGER NOT NULL, zvol TEXT NOT NULL, size INTEGER, blocksize INTEGER, mapped INTEGER DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP);`,
		// RBAC tables
		`CREATE TABLE IF NOT EXISTS roles (id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP);`,
		`CREATE TABLE IF NOT EXISTS permissions (id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP);`,
		`CREATE TABLE IF NOT EXISTS role_permissions (role_id TEXT NOT NULL, permission_id TEXT NOT NULL, PRIMARY KEY (role_id, permission_id), FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE, FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE);`,
		`CREATE TABLE IF NOT EXISTS user_roles (user_id TEXT NOT NULL, role_id TEXT NOT NULL, PRIMARY KEY (user_id, role_id), FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE);`,
		`CREATE TABLE IF NOT EXISTS sessions (id TEXT PRIMARY KEY, user_id TEXT NOT NULL, token TEXT NOT NULL UNIQUE, expires_at DATETIME NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE);`,
		`CREATE INDEX IF NOT EXISTS idx_sessions_token ON sessions(token);`,
		`CREATE INDEX IF NOT EXISTS idx_sessions_user_id ON sessions(user_id);`,
		`CREATE INDEX IF NOT EXISTS idx_sessions_expires_at ON sessions(expires_at);`,
	}
	for _, s := range stmts {
		if _, err := tx.ExecContext(ctx, s); err != nil {
			return err
		}
	}

	// Enhance audit_events table if needed (add missing columns)
	enhanceAuditTable(ctx, tx)

	// Seed default roles and permissions
	if err := seedRolesAndPermissions(ctx, tx); err != nil {
		return err
	}

	// Seed a default admin user if not exists
	var count int
	if err := tx.QueryRowContext(ctx, `SELECT COUNT(1) FROM users WHERE username = 'admin'`).Scan(&count); err != nil {
		log.Printf("seed check failed: %v", err)
	}
	if count == 0 {
		// note: simple seeded password: admin (do not use in prod)
		pwHash, err := auth.HashPassword("admin")
		if err != nil {
			return err
		}
		if _, err := tx.ExecContext(ctx, `INSERT INTO users (id, username, password_hash, role) VALUES (?, 'admin', ?, 'admin')`, "admin", pwHash); err != nil {
			return err
		}
		// Assign admin role to admin user
		var adminRoleID string
		if err := tx.QueryRowContext(ctx, `SELECT id FROM roles WHERE name = 'admin'`).Scan(&adminRoleID); err == nil {
			tx.ExecContext(ctx, `INSERT OR IGNORE INTO user_roles (user_id, role_id) VALUES (?, ?)`, "admin", adminRoleID)
		}
	}
	if err := tx.Commit(); err != nil {
		return err
	}
	return nil
}

func enhanceAuditTable(ctx context.Context, tx *sql.Tx) {
	// Check if columns exist and add them if missing
	// SQLite doesn't support IF NOT EXISTS for ALTER TABLE, so we'll try-catch
	columns := []struct {
		name string
		stmt string
	}{
		{"actor", `ALTER TABLE audit_events ADD COLUMN actor TEXT;`},
		{"resource", `ALTER TABLE audit_events ADD COLUMN resource TEXT;`},
		{"payload_hash", `ALTER TABLE audit_events ADD COLUMN payload_hash TEXT;`},
		{"result", `ALTER TABLE audit_events ADD COLUMN result TEXT;`},
		{"client_ip", `ALTER TABLE audit_events ADD COLUMN client_ip TEXT;`},
	}

	for _, col := range columns {
		_, err := tx.ExecContext(ctx, col.stmt)
		if err != nil {
			// Column might already exist, ignore error
			log.Printf("Note: %s column may already exist: %v", col.name, err)
		}
	}
}

func seedRolesAndPermissions(ctx context.Context, tx *sql.Tx) error {
	// Seed default roles
	roles := []struct {
		id          string
		name        string
		description string
	}{
		{"admin", "admin", "Administrator with full access"},
		{"operator", "operator", "Operator with limited administrative access"},
		{"viewer", "viewer", "Read-only access"},
	}

	for _, r := range roles {
		_, err := tx.ExecContext(ctx, `INSERT OR IGNORE INTO roles (id, name, description) VALUES (?, ?, ?)`, r.id, r.name, r.description)
		if err != nil {
			return err
		}
	}

	// Seed default permissions
	permissions := []struct {
		id          string
		name        string
		description string
	}{
		{"storage.pool.create", "storage.pool.create", "Create storage pools"},
		{"storage.pool.scrub", "storage.pool.scrub", "Scrub storage pools"},
		{"storage.dataset.create", "storage.dataset.create", "Create datasets"},
		{"storage.dataset.snapshot", "storage.dataset.snapshot", "Create snapshots"},
		{"shares.nfs.create", "shares.nfs.create", "Create NFS shares"},
		{"shares.nfs.delete", "shares.nfs.delete", "Delete NFS shares"},
		{"shares.smb.create", "shares.smb.create", "Create SMB shares"},
		{"shares.smb.delete", "shares.smb.delete", "Delete SMB shares"},
		{"iscsi.target.create", "iscsi.target.create", "Create iSCSI targets"},
		{"iscsi.lun.create", "iscsi.lun.create", "Create iSCSI LUNs"},
		{"iscsi.lun.delete", "iscsi.lun.delete", "Delete iSCSI LUNs"},
		{"iscsi.lun.unmap", "iscsi.lun.unmap", "Unmap iSCSI LUNs"},
		{"iscsi.portal.create", "iscsi.portal.create", "Add iSCSI portals"},
		{"iscsi.initiator.create", "iscsi.initiator.create", "Add iSCSI initiators"},
		{"users.manage", "users.manage", "Manage users"},
		{"roles.manage", "roles.manage", "Manage roles and permissions"},
	}

	for _, p := range permissions {
		_, err := tx.ExecContext(ctx, `INSERT OR IGNORE INTO permissions (id, name, description) VALUES (?, ?, ?)`, p.id, p.name, p.description)
		if err != nil {
			return err
		}
	}

	// Assign all permissions to admin role
	var adminRoleID string
	if err := tx.QueryRowContext(ctx, `SELECT id FROM roles WHERE name = 'admin'`).Scan(&adminRoleID); err != nil {
		return err
	}

	for _, p := range permissions {
		_, err := tx.ExecContext(ctx, `INSERT OR IGNORE INTO role_permissions (role_id, permission_id) VALUES (?, ?)`, adminRoleID, p.id)
		if err != nil {
			return err
		}
	}

	// Assign some permissions to operator role
	var operatorRoleID string
	if err := tx.QueryRowContext(ctx, `SELECT id FROM roles WHERE name = 'operator'`).Scan(&operatorRoleID); err == nil {
		operatorPerms := []string{
			"storage.pool.create",
			"storage.dataset.create",
			"storage.dataset.snapshot",
			"shares.nfs.create",
			"shares.nfs.delete",
			"shares.smb.create",
			"shares.smb.delete",
			"iscsi.target.create",
			"iscsi.lun.create",
			"iscsi.lun.delete",
			"iscsi.portal.create",
			"iscsi.initiator.create",
		}
		for _, permID := range operatorPerms {
			tx.ExecContext(ctx, `INSERT OR IGNORE INTO role_permissions (role_id, permission_id) VALUES (?, ?)`, operatorRoleID, permID)
		}
	}

	return nil
}
