import { db, now } from '../db.js';
import { camelJob } from '../models/mappers.js';
import { listSystemJobActions, recordSystemJobAction } from '../models/systemJobModel.js';
import { syncDynamicHostGroups } from '../models/hostGroupModel.js';
import { runCatalogChecks } from './catalogWorker.js';
import { runAutoPromotions } from './promotionWorker.js';
import { cancelExecutionJob, appendJobLog, activeExecutionJobIds } from './powershellRunner.js';

const backgroundRuns = new Map();

const BACKGROUND_JOBS = [
  {
    id: 'worker:host-group-sync',
    name: 'Dynamic Host Group Sync',
    category: 'Inventory',
    description: 'Reconciles rule-based Host Groups against the current Host Library.',
    run: async ({ signal } = {}) => syncDynamicHostGroups({ signal })
  },
  {
    id: 'worker:catalog-auto-check',
    name: 'Application Catalog Auto-Check',
    category: 'Applications',
    description: 'Checks opted-in application records for newer upstream versions.',
    run: async ({ signal } = {}) => runCatalogChecks({ signal })
  },
  {
    id: 'worker:intune-auto-promote',
    name: 'Intune Auto-Promotion',
    category: 'Intune',
    description: 'Promotes deployment rings when configured soak windows elapse.',
    run: async ({ signal } = {}) => runAutoPromotions({ signal })
  }
];

export function listSystemJobs() {
  return {
    jobs: [...executionJobs(), ...backgroundJobs()],
    actions: listSystemJobActions(200),
    activeExecutionJobIds: activeExecutionJobIds()
  };
}

export async function runSystemJob(targetId, actorUserId) {
  const definition = BACKGROUND_JOBS.find((job) => job.id === targetId);
  if (!definition) {
    const action = recordSystemJobAction({
      targetType: 'system',
      targetId,
      action: 'run-now',
      status: 'failed',
      message: 'System job not found.',
      actorUserId
    });
    return { ok: false, action, error: 'System job not found.' };
  }
  if (backgroundRuns.get(targetId)?.running) {
    const action = recordSystemJobAction({
      targetType: 'system',
      targetId,
      action: 'run-now',
      status: 'skipped',
      message: `${definition.name} is already running.`,
      actorUserId
    });
    return { ok: false, action, error: `${definition.name} is already running.` };
  }

  const controller = new AbortController();
  backgroundRuns.set(targetId, {
    running: true,
    cancelRequested: false,
    startedAt: now(),
    lastError: '',
    lastSummary: null,
    controller
  });
  const startAction = recordSystemJobAction({
    targetType: 'system',
    targetId,
    action: 'run-now',
    status: 'started',
    message: `${definition.name} started by operator.`,
    actorUserId
  });
  try {
    const summary = await definition.run({ signal: controller.signal });
    const canceled = Boolean(backgroundRuns.get(targetId)?.cancelRequested || controller.signal.aborted);
    backgroundRuns.set(targetId, {
      running: false,
      cancelRequested: false,
      startedAt: '',
      lastRunAt: now(),
      lastError: canceled ? 'Canceled by operator.' : '',
      lastSummary: summary
    });
    const action = recordSystemJobAction({
      targetType: 'system',
      targetId,
      action: 'run-now',
      status: canceled ? 'canceled' : 'success',
      message: canceled ? `${definition.name} canceled by operator.` : `${definition.name} completed.`,
      actorUserId,
      metadata: { summary, startActionId: startAction.id }
    });
    return { ok: !canceled, action, summary, canceled };
  } catch (error) {
    const canceled = Boolean(backgroundRuns.get(targetId)?.cancelRequested || controller.signal.aborted);
    backgroundRuns.set(targetId, {
      running: false,
      cancelRequested: false,
      startedAt: '',
      lastRunAt: now(),
      lastError: canceled ? 'Canceled by operator.' : error.message,
      lastSummary: null
    });
    const action = recordSystemJobAction({
      targetType: 'system',
      targetId,
      action: 'run-now',
      status: canceled ? 'canceled' : 'failed',
      message: canceled ? `${definition.name} canceled by operator.` : error.message,
      actorUserId,
      metadata: { startActionId: startAction.id }
    });
    return { ok: false, action, error: canceled ? 'Canceled by operator.' : error.message, canceled };
  }
}

export function cancelSystemJob(targetId, actorUserId, reason = '') {
  if (targetId.startsWith('worker:')) {
    const definition = BACKGROUND_JOBS.find((job) => job.id === targetId);
    const state = backgroundRuns.get(targetId);
    if (!definition || !state?.running) {
      const action = recordSystemJobAction({
        targetType: 'system',
        targetId,
        action: 'cancel',
        status: 'failed',
        message: definition ? `${definition.name} is not running.` : 'System job not found.',
        actorUserId,
        metadata: { reason }
      });
      return { ok: false, action, error: action.message };
    }
    state.cancelRequested = true;
    state.controller?.abort?.(new Error(reason || 'Canceled by operator.'));
    backgroundRuns.set(targetId, state);
    const action = recordSystemJobAction({
      targetType: 'system',
      targetId,
      action: 'cancel',
      status: 'success',
      message: `Cancel requested for ${definition.name}.`,
      actorUserId,
      metadata: { reason }
    });
    return { ok: true, action, result: { canceled: true, targetType: 'background' } };
  }

  if (!targetId.startsWith('job_')) {
    const action = recordSystemJobAction({
      targetType: 'system',
      targetId,
      action: 'cancel',
      status: 'failed',
      message: 'Only running script execution jobs can be canceled.',
      actorUserId,
      metadata: { reason }
    });
    return { ok: false, action, error: 'Only running script execution jobs can be canceled.' };
  }
  const result = cancelExecutionJob(targetId, { actorUserId, reason });
  const status = result.canceled ? 'success' : 'failed';
  const message = result.canceled
    ? `Cancel requested; signaled ${result.signaledProcesses || 0} running process(es).`
    : result.reason;
  const action = recordSystemJobAction({
    jobId: targetId,
    targetType: 'execution',
    targetId,
    action: 'cancel',
    status,
    message,
    actorUserId,
    metadata: { reason, result }
  });
  if (result.canceled) appendJobLog(targetId, 'system', `Job management action logged: cancel (${action.id}).`, null);
  return { ok: result.canceled, action, result, error: result.canceled ? null : result.reason };
}

function executionJobs() {
  const rows = db.prepare(`
    SELECT j.*, r.name AS runplan_name, s.name AS script_name,
           COUNT(jh.id) AS host_count,
           SUM(CASE WHEN jh.status = 'running' THEN 1 ELSE 0 END) AS running_hosts,
           SUM(CASE WHEN jh.status = 'queued' THEN 1 ELSE 0 END) AS queued_hosts
    FROM jobs j
    LEFT JOIN runplans r ON r.id = j.runplan_id
    LEFT JOIN scripts s ON s.id = j.script_id
    LEFT JOIN job_hosts jh ON jh.job_id = j.id
    GROUP BY j.id
    ORDER BY j.created_at DESC
    LIMIT 200
  `).all();
  return rows.map((row) => {
    const job = camelJob(row);
    const running = ['queued', 'running'].includes(job.status);
    return {
      id: job.id,
      type: 'execution',
      category: 'Script Execution',
      name: job.runplanName || job.scriptName || job.id,
      description: job.runplanName ? `RunPlan ${job.runplanName}` : `Script test ${job.scriptName || job.id}`,
      status: job.status,
      canCancel: running,
      canRunNow: false,
      startedAt: job.startedAt || '',
      endedAt: job.endedAt || '',
      createdAt: job.createdAt,
      hostCount: row.host_count || 0,
      runningHosts: row.running_hosts || 0,
      queuedHosts: row.queued_hosts || 0,
      metadata: {
        runplanId: job.runplanId,
        scriptId: job.scriptId,
        triggeredBy: job.triggeredBy
      }
    };
  });
}

function backgroundJobs() {
  return BACKGROUND_JOBS.map((job) => {
    const state = backgroundRuns.get(job.id) || {};
    return {
      id: job.id,
      type: 'background',
      category: job.category,
      name: job.name,
      description: job.description,
      status: state.cancelRequested ? 'canceling' : state.running ? 'running' : 'idle',
      canCancel: Boolean(state.running),
      canRunNow: !state.running,
      startedAt: state.startedAt || '',
      endedAt: '',
      createdAt: '',
      hostCount: 0,
      runningHosts: 0,
      queuedHosts: 0,
      metadata: {
        lastRunAt: state.lastRunAt || '',
        lastError: state.lastError || '',
        lastSummary: state.lastSummary || null
      }
    };
  });
}
