/**
 * Backup Repository
 * Handles CRUD operations for backups and backup_schedules tables
 */

import { BaseRepository } from '../base.repository.ts';
import type {
  IBackup,
  IBackupSchedule,
  IBackupScheduleUpdate,
  TPlatformServiceType,
  TBackupScheduleScope,
  IRetentionPolicy,
} from '../../types.ts';

export class BackupRepository extends BaseRepository {
  // ============ Backup CRUD ============

  create(backup: Omit<IBackup, 'id'>): IBackup {
    this.query(
      `INSERT INTO backups (
        service_id, service_name, filename, size_bytes, created_at,
        includes_image, platform_resources, checksum, schedule_id,
        snapshot_id, stored_size_bytes
      ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
      [
        backup.serviceId,
        backup.serviceName,
        backup.filename,
        backup.sizeBytes,
        backup.createdAt,
        backup.includesImage ? 1 : 0,
        JSON.stringify(backup.platformResources),
        backup.checksum,
        backup.scheduleId ?? null,
        backup.snapshotId ?? null,
        backup.storedSizeBytes ?? 0,
      ]
    );

    // Get the created backup by looking for the most recent one with matching filename
    const rows = this.query(
      'SELECT * FROM backups WHERE filename = ? ORDER BY id DESC LIMIT 1',
      [backup.filename]
    );

    return this.rowToBackup(rows[0]);
  }

  getById(id: number): IBackup | null {
    const rows = this.query('SELECT * FROM backups WHERE id = ?', [id]);
    return rows.length > 0 ? this.rowToBackup(rows[0]) : null;
  }

  getByService(serviceId: number): IBackup[] {
    const rows = this.query(
      'SELECT * FROM backups WHERE service_id = ? ORDER BY created_at DESC',
      [serviceId]
    );
    return rows.map((row) => this.rowToBackup(row));
  }

  getAll(): IBackup[] {
    const rows = this.query('SELECT * FROM backups ORDER BY created_at DESC');
    return rows.map((row) => this.rowToBackup(row));
  }

  delete(id: number): void {
    this.query('DELETE FROM backups WHERE id = ?', [id]);
  }

  deleteByService(serviceId: number): void {
    this.query('DELETE FROM backups WHERE service_id = ?', [serviceId]);
  }

  getBySchedule(scheduleId: number): IBackup[] {
    const rows = this.query(
      'SELECT * FROM backups WHERE schedule_id = ? ORDER BY created_at DESC',
      [scheduleId]
    );
    return rows.map((row) => this.rowToBackup(row));
  }

  getBySnapshotId(snapshotId: string): IBackup | null {
    const rows = this.query(
      'SELECT * FROM backups WHERE snapshot_id = ?',
      [snapshotId]
    );
    return rows.length > 0 ? this.rowToBackup(rows[0]) : null;
  }

  private rowToBackup(row: any): IBackup {
    let platformResources: TPlatformServiceType[] = [];
    const platformResourcesRaw = row.platform_resources;
    if (platformResourcesRaw) {
      try {
        platformResources = JSON.parse(String(platformResourcesRaw));
      } catch {
        platformResources = [];
      }
    }

    return {
      id: Number(row.id),
      serviceId: Number(row.service_id),
      serviceName: String(row.service_name),
      filename: String(row.filename),
      snapshotId: row.snapshot_id ? String(row.snapshot_id) : undefined,
      sizeBytes: Number(row.size_bytes),
      storedSizeBytes: row.stored_size_bytes ? Number(row.stored_size_bytes) : undefined,
      createdAt: Number(row.created_at),
      includesImage: Boolean(row.includes_image),
      platformResources,
      checksum: String(row.checksum),
      scheduleId: row.schedule_id ? Number(row.schedule_id) : undefined,
    };
  }

  // ============ Backup Schedule CRUD ============

  createSchedule(schedule: Omit<IBackupSchedule, 'id'>): IBackupSchedule {
    const now = Date.now();
    this.query(
      `INSERT INTO backup_schedules (
        scope_type, scope_pattern, service_id, service_name, cron_expression,
        retention_hourly, retention_daily, retention_weekly, retention_monthly,
        enabled, last_run_at, next_run_at, last_status, last_error, created_at, updated_at
      ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
      [
        schedule.scopeType,
        schedule.scopePattern ?? null,
        schedule.serviceId ?? null,
        schedule.serviceName ?? null,
        schedule.cronExpression,
        schedule.retention.hourly,
        schedule.retention.daily,
        schedule.retention.weekly,
        schedule.retention.monthly,
        schedule.enabled ? 1 : 0,
        schedule.lastRunAt,
        schedule.nextRunAt,
        schedule.lastStatus,
        schedule.lastError,
        now,
        now,
      ]
    );

    // Get the created schedule by looking for the most recent one with matching scope
    const rows = this.query(
      'SELECT * FROM backup_schedules WHERE scope_type = ? AND cron_expression = ? ORDER BY id DESC LIMIT 1',
      [schedule.scopeType, schedule.cronExpression]
    );

    return this.rowToSchedule(rows[0]);
  }

  getScheduleById(id: number): IBackupSchedule | null {
    const rows = this.query('SELECT * FROM backup_schedules WHERE id = ?', [id]);
    return rows.length > 0 ? this.rowToSchedule(rows[0]) : null;
  }

  getSchedulesByService(serviceId: number): IBackupSchedule[] {
    const rows = this.query(
      'SELECT * FROM backup_schedules WHERE service_id = ? ORDER BY created_at DESC',
      [serviceId]
    );
    return rows.map((row) => this.rowToSchedule(row));
  }

  getEnabledSchedules(): IBackupSchedule[] {
    const rows = this.query(
      'SELECT * FROM backup_schedules WHERE enabled = 1 ORDER BY next_run_at ASC'
    );
    return rows.map((row) => this.rowToSchedule(row));
  }

  getAllSchedules(): IBackupSchedule[] {
    const rows = this.query('SELECT * FROM backup_schedules ORDER BY created_at DESC');
    return rows.map((row) => this.rowToSchedule(row));
  }

  updateSchedule(id: number, updates: IBackupScheduleUpdate & { lastRunAt?: number; nextRunAt?: number; lastStatus?: 'success' | 'failed' | null; lastError?: string | null }): void {
    const setClauses: string[] = [];
    const params: (string | number | null)[] = [];

    if (updates.cronExpression !== undefined) {
      setClauses.push('cron_expression = ?');
      params.push(updates.cronExpression);
    }
    if (updates.retention !== undefined) {
      setClauses.push('retention_hourly = ?');
      params.push(updates.retention.hourly);
      setClauses.push('retention_daily = ?');
      params.push(updates.retention.daily);
      setClauses.push('retention_weekly = ?');
      params.push(updates.retention.weekly);
      setClauses.push('retention_monthly = ?');
      params.push(updates.retention.monthly);
    }
    if (updates.enabled !== undefined) {
      setClauses.push('enabled = ?');
      params.push(updates.enabled ? 1 : 0);
    }
    if (updates.lastRunAt !== undefined) {
      setClauses.push('last_run_at = ?');
      params.push(updates.lastRunAt);
    }
    if (updates.nextRunAt !== undefined) {
      setClauses.push('next_run_at = ?');
      params.push(updates.nextRunAt);
    }
    if (updates.lastStatus !== undefined) {
      setClauses.push('last_status = ?');
      params.push(updates.lastStatus);
    }
    if (updates.lastError !== undefined) {
      setClauses.push('last_error = ?');
      params.push(updates.lastError);
    }

    if (setClauses.length === 0) return;

    setClauses.push('updated_at = ?');
    params.push(Date.now());
    params.push(id);

    this.query(`UPDATE backup_schedules SET ${setClauses.join(', ')} WHERE id = ?`, params);
  }

  deleteSchedule(id: number): void {
    this.query('DELETE FROM backup_schedules WHERE id = ?', [id]);
  }

  deleteSchedulesByService(serviceId: number): void {
    this.query('DELETE FROM backup_schedules WHERE service_id = ?', [serviceId]);
  }

  private rowToSchedule(row: any): IBackupSchedule {
    return {
      id: Number(row.id),
      scopeType: (String(row.scope_type) || 'service') as TBackupScheduleScope,
      scopePattern: row.scope_pattern ? String(row.scope_pattern) : undefined,
      serviceId: row.service_id ? Number(row.service_id) : undefined,
      serviceName: row.service_name ? String(row.service_name) : undefined,
      cronExpression: String(row.cron_expression),
      retention: {
        hourly: Number(row.retention_hourly ?? 0),
        daily: Number(row.retention_daily ?? 7),
        weekly: Number(row.retention_weekly ?? 4),
        monthly: Number(row.retention_monthly ?? 12),
      } as IRetentionPolicy,
      enabled: Boolean(row.enabled),
      lastRunAt: row.last_run_at ? Number(row.last_run_at) : null,
      nextRunAt: row.next_run_at ? Number(row.next_run_at) : null,
      lastStatus: row.last_status ? (String(row.last_status) as 'success' | 'failed') : null,
      lastError: row.last_error ? String(row.last_error) : null,
      createdAt: Number(row.created_at),
      updatedAt: Number(row.updated_at),
    };
  }
}
