import { getCollection } from '../../lib/datastore.js';
import { config } from '../../lib/config.js';
import { registerJob } from './registry.js';
import type { JobContext, JobResult } from './types.js';
import type { SessionDoc } from '../sessions/types.js';
import type { SubscriptionDoc } from '../subscriptions/types.js';
import type { UsageDoc } from '../usage/types.js';
import type { LicenseDoc } from '../licenses/types.js';

// ── Built-In Jobs ────────────────────────────────────────────
// Registered at service startup. Each job has a handler that
// performs the actual work when triggered by the runner.

/**
 * Register all built-in jobs with the job registry.
 */
export function registerBuiltInJobs(): void {
  registerJob('trial-expiration-check', trialExpirationCheck);
  registerJob('usage-quota-reset', usageQuotaReset);
  registerJob('stale-session-cleanup', staleSessionCleanup);
  registerJob('telemetry-ttl-sweep', telemetryTtlSweep);
  registerJob('waitlist-reminder', waitlistReminder);
  registerJob('license-expiry-check', licenseExpiryCheck);
  registerJob('devintelli-daily-sync', devintelliDailySync);
}

/**
 * Default cron definitions for built-in jobs.
 */
export const BUILT_IN_JOB_DEFAULTS = [
  {
    name: 'trial-expiration-check',
    cron: '0 * * * *',
    description: 'Check for expired trials (hourly)',
    timeoutMs: 120_000,
  },
  {
    name: 'usage-quota-reset',
    cron: '0 0 * * *',
    description: 'Reset daily usage quotas (midnight UTC)',
    timeoutMs: 120_000,
  },
  {
    name: 'stale-session-cleanup',
    cron: '0 */6 * * *',
    description: 'Clean up stale sessions (every 6 hours)',
    timeoutMs: 180_000,
  },
  {
    name: 'telemetry-ttl-sweep',
    cron: '0 3 * * *',
    description: 'Sweep telemetry events past TTL (3am UTC)',
    timeoutMs: 300_000,
  },
  {
    name: 'waitlist-reminder',
    cron: '0 9 * * 1',
    description: 'Flag stale waitlist entries (Monday 9am UTC)',
    timeoutMs: 120_000,
  },
  {
    name: 'license-expiry-check',
    cron: '0 8 * * *',
    description: 'Warn users with expiring licenses (8am UTC)',
    timeoutMs: 120_000,
  },
  {
    name: 'devintelli-daily-sync',
    cron: '0 6 * * *',
    description: 'DevIntelli daily GitHub sync (6am UTC)',
    timeoutMs: 300_000,
  },
] as const;

// ── Job Implementations ──────────────────────────────────────

/** Helper to get sessions collection */
function sessionsColl() {
  return getCollection<SessionDoc>('sessions', '/userId');
}

/** Helper to get subscriptions collection */
function subsColl() {
  return getCollection<SubscriptionDoc>('subscriptions', '/userId');
}

/** Helper to get usage collection */
function usageColl() {
  return getCollection<UsageDoc>('usage_daily', '/userId');
}

/** Helper to get licenses collection */
function licensesColl() {
  return getCollection<LicenseDoc>('licenses', '/userId');
}

async function trialExpirationCheck(ctx: JobContext): Promise<JobResult> {
  ctx.log.info({ jobName: ctx.jobName }, '[jobs] Running trial expiration check');
  const now = new Date().toISOString();
  let checked = 0;
  let expired = 0;

  try {
    // Find all trialing subscriptions past currentPeriodEnd
    const subs = await subsColl().findMany({
      filter: { status: 'trialing' },
      limit: 1000,
    });

    for (const sub of subs) {
      checked++;
      if (sub.currentPeriodEnd && sub.currentPeriodEnd < now) {
        // Mark as expired
        await subsColl().update(sub.id, sub.userId, {
          status: 'past_due',
          updatedAt: now,
        } as Partial<SubscriptionDoc>);
        expired++;
      }
    }
  } catch (err) {
    ctx.log.warn({ err }, '[jobs] Trial expiration check failed');
  }

  return {
    success: true,
    message: `Checked ${checked} trials, expired ${expired}`,
    metrics: { checked, expired },
  };
}

async function usageQuotaReset(ctx: JobContext): Promise<JobResult> {
  ctx.log.info({ jobName: ctx.jobName }, '[jobs] Running usage quota reset');
  const today = new Date().toISOString().slice(0, 10);
  let reset = 0;

  try {
    // Get all usage docs for today that need reset
    // Cosmos doesn't support partial updates well, so we flag them
    const docs = await usageColl().findMany({
      filter: { date: today },
      limit: 10000,
    });

    // Reset counters is handled by date-based keys - new day = new doc
    // This job primarily ensures daily aggregation runs
    reset = docs.length;
  } catch (err) {
    ctx.log.warn({ err }, '[jobs] Usage quota reset failed');
  }

  return { success: true, message: `Processed ${reset} usage records`, metrics: { reset } };
}

async function staleSessionCleanup(ctx: JobContext): Promise<JobResult> {
  ctx.log.info({ jobName: ctx.jobName }, '[jobs] Running stale session cleanup');
  const now = new Date();
  const staleThreshold = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000).toISOString(); // 7 days
  let removed = 0;

  try {
    // Find expired or stale sessions
    const sessions = await sessionsColl().findMany({
      filter: {},
      limit: 5000,
    });

    for (const session of sessions) {
      const isExpired = new Date(session.expiresAt) < now;
      const isStale = session.lastActiveAt && session.lastActiveAt < staleThreshold;

      if (isExpired || isStale) {
        // Soft delete by revoking
        await sessionsColl().update(session.id, session.userId, {
          revokedAt: now.toISOString(),
        } as Partial<SessionDoc>);
        removed++;
      }
    }
  } catch (err) {
    ctx.log.warn({ err }, '[jobs] Stale session cleanup failed');
  }

  return { success: true, message: `Removed ${removed} stale sessions`, metrics: { removed } };
}

async function telemetryTtlSweep(ctx: JobContext): Promise<JobResult> {
  ctx.log.info({ jobName: ctx.jobName }, '[jobs] Running telemetry TTL sweep');
  // Cosmos TTL is best-effort, this ensures cleanup tracking
  // Actual deletion is handled by Cosmos TTL policy
  return { success: true, message: 'Telemetry TTL sweep completed', metrics: { deleted: 0 } };
}

async function waitlistReminder(ctx: JobContext): Promise<JobResult> {
  ctx.log.info({ jobName: ctx.jobName }, '[jobs] Running waitlist reminder');
  let flagged = 0;

  try {
    // Find pending waitlist entries older than 30 days
    const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString();

    const { getCollection } = await import('../../lib/datastore.js');
    type WaitlistDoc = {
      id: string;
      email: string;
      status: string;
      createdAt: string;
      productId: string;
    };
    const waitlistColl = getCollection<WaitlistDoc>('waitlist', '/email');

    const entries = await waitlistColl.findMany({
      filter: { status: 'pending', createdAt: { $lt: thirtyDaysAgo } },
      limit: 1000,
    });

    // Flag for follow-up (could trigger notification in future)
    flagged = entries.length;
  } catch (err) {
    ctx.log.warn({ err }, '[jobs] Waitlist reminder failed');
  }

  return {
    success: true,
    message: `Flagged ${flagged} stale waitlist entries`,
    metrics: { flagged },
  };
}

async function licenseExpiryCheck(ctx: JobContext): Promise<JobResult> {
  ctx.log.info({ jobName: ctx.jobName }, '[jobs] Running license expiry check');
  const now = new Date();
  const warnThreshold = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString(); // 7 days
  let warned = 0;

  try {
    // Find active licenses expiring within 7 days
    const licenses = await licensesColl().findMany({
      filter: { status: 'active' },
      limit: 1000,
    });

    for (const license of licenses) {
      if (
        license.expiresAt &&
        license.expiresAt < warnThreshold &&
        license.expiresAt > now.toISOString()
      ) {
        // Could trigger notification here via event bus
        warned++;
      }
    }
  } catch (err) {
    ctx.log.warn({ err }, '[jobs] License expiry check failed');
  }

  return { success: true, message: `Found ${warned} licenses expiring soon`, metrics: { warned } };
}

async function devintelliDailySync(ctx: JobContext): Promise<JobResult> {
  ctx.log.info({ jobName: ctx.jobName }, '[jobs] Running DevIntelli daily GitHub sync');

  const baseUrl = config.DEVINTELLI_BACKEND_URL;
  const apiKey = config.DEVINTELLI_INTERNAL_API_KEY;
  const url = `${baseUrl}/api/sync/daily`;

  try {
    const res = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-internal-key': apiKey,
      },
      signal: AbortSignal.timeout(240_000),
    });

    if (!res.ok) {
      const body = await res.text().catch(() => '');
      return {
        success: false,
        message: `DevIntelli backend returned ${res.status}: ${body.slice(0, 200)}`,
        metrics: { statusCode: res.status },
      };
    }

    const data = (await res.json()) as Record<string, unknown>;
    return {
      success: true,
      message: `Triggered ${data.triggered ?? 0} syncs, skipped ${data.skipped ?? 0}`,
      metrics: {
        totalConnections: data.totalConnections,
        triggered: data.triggered,
        skipped: data.skipped,
      },
    };
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    ctx.log.error({ err, url }, '[jobs] DevIntelli daily sync failed');
    return { success: false, message: msg };
  }
}
