import path from 'node:path';
import { access, mkdir, rm } from 'node:fs/promises';
import { pathToFileURL } from 'node:url';
import { getEnv } from '../config/env.js';
import { sendManagedStockAlertWebPush } from './stock-alert-service.js';

export type ManagedScheduleServiceResult = {
  ok: boolean;
  skipped: boolean;
  title: string;
  body: string;
  itemCount: number;
  lines: string[];
  ios: {
    ok: boolean;
    skipped: boolean;
    reason?: string;
    sentCount: number;
    failedCount: number;
  };
  web: {
    ok: boolean;
    skipped: boolean;
    reason?: string;
    sentCount: number;
    failedCount: number;
  };
};

export type ManagedScheduleServiceMetadata = {
  scheduleId: number;
  serviceKey: string;
  packageName: string;
  relativeDirectory: string;
  manifestPath: string;
  readmePath: string;
  sourcePath: string;
  runtimePath: string;
};

function sanitizeManagedServiceToken(value: string) {
  return value
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '')
    .slice(0, 60);
}

function appendScheduleSuffixOnce(value: string, suffix: string) {
  const normalizedValue = value.trim();
  const normalizedSuffix = suffix.trim().toLowerCase();

  if (!normalizedValue || !normalizedSuffix) {
    return normalizedValue;
  }

  return normalizedValue.toLowerCase().endsWith(`-${normalizedSuffix}`)
    ? normalizedValue
    : `${normalizedValue}-${suffix.trim()}`;
}

function getScheduleRepoRoot() {
  const env = getEnv();
  return env.SERVER_COMMAND_MAIN_PROJECT_ROOT || env.PLAN_MAIN_PROJECT_REPO_PATH || process.cwd();
}

export function buildManagedScheduleServiceMetadata(scheduleId: number, workId: string): ManagedScheduleServiceMetadata {
  const workToken = sanitizeManagedServiceToken(appendScheduleSuffixOnce(workId, 'service')) || 'task-service';
  const serviceKey = `schedule-${scheduleId}-${workToken}`;
  const relativeDirectory = `.auto_codex/schedule/${scheduleId}`;
  const packageName = workToken || `schedule-${scheduleId}-service`;

  return {
    scheduleId,
    serviceKey,
    packageName,
    relativeDirectory,
    manifestPath: `${relativeDirectory}/service-manifest.json`,
    readmePath: `${relativeDirectory}/README.md`,
    sourcePath: `${relativeDirectory}/service.ts`,
    runtimePath: `${relativeDirectory}/service.mjs`,
  };
}

export async function prepareManagedScheduleServiceDirectory(scheduleId: number) {
  const repoRoot = getScheduleRepoRoot();
  const absoluteDirectory = path.join(repoRoot, '.auto_codex', 'schedule', String(scheduleId));

  await mkdir(absoluteDirectory, { recursive: true });
  await rm(path.join(absoluteDirectory, 'managed-service'), { recursive: true, force: true });
}

export async function removeManagedScheduleServiceArtifacts(scheduleId: number) {
  const repoRoot = getScheduleRepoRoot();
  const scheduleDirectory = path.join(repoRoot, '.auto_codex', 'schedule', String(scheduleId));

  await rm(path.join(scheduleDirectory, 'managed-service'), { recursive: true, force: true });
  await rm(path.join(scheduleDirectory, 'README.md'), { force: true });
  await rm(path.join(scheduleDirectory, 'service-manifest.json'), { force: true });
  await rm(path.join(scheduleDirectory, 'service.ts'), { force: true });
  await rm(path.join(scheduleDirectory, 'service.mjs'), { force: true });
}

export async function hasManagedScheduleServicePackage(relativeDirectory: string | null | undefined) {
  const trimmedDirectory = String(relativeDirectory ?? '').trim();

  if (!trimmedDirectory) {
    return false;
  }

  const repoRoot = getScheduleRepoRoot();
  const runtimePath = path.join(repoRoot, trimmedDirectory, 'service.mjs');
  const manifestPath = path.join(repoRoot, trimmedDirectory, 'service-manifest.json');

  try {
    await Promise.all([access(runtimePath), access(manifestPath)]);
    return true;
  } catch {
    return false;
  }
}

export async function runManagedScheduleService(relativeDirectory: string) {
  const repoRoot = getScheduleRepoRoot();
  const runtimePath = path.join(repoRoot, relativeDirectory, 'service.mjs');
  const importedModule = await import(`${pathToFileURL(runtimePath).href}?t=${Date.now()}`);
  const run =
    typeof importedModule.run === 'function'
      ? importedModule.run
      : typeof importedModule.default?.run === 'function'
        ? importedModule.default.run
        : null;

  if (!run) {
    throw new Error(`스케줄 서비스 실행 함수를 찾을 수 없습니다: ${relativeDirectory}/service.mjs`);
  }

  return run({
    scheduleRoot: path.join(repoRoot, trimmedRelativeDirectory(relativeDirectory)),
    scheduleDirectory: trimmedRelativeDirectory(relativeDirectory),
    repoRoot,
    now: new Date().toISOString(),
    runCurrentPriceStockAlertService(definition: { scheduleId: number; serviceKey: string; title: string }) {
      return sendManagedStockAlertWebPush({
        scheduleId: definition.scheduleId,
        serviceKey: definition.serviceKey,
        title: definition.title,
        mode: 'price',
      });
    },
    runChangeRateThresholdStockAlertService(
      definition: { scheduleId: number; serviceKey: string; title: string; thresholdPercent: number },
    ) {
      return sendManagedStockAlertWebPush({
        scheduleId: definition.scheduleId,
        serviceKey: definition.serviceKey,
        title: definition.title,
        mode: 'change-threshold',
        thresholdPercent: definition.thresholdPercent,
      });
    },
    runChangeRateAndVolumeSpikeStockAlertService(
      definition: {
        scheduleId: number;
        serviceKey: string;
        title: string;
        thresholdPercent: number;
        minVolumeIncreasePercent: number;
      },
    ) {
      return sendManagedStockAlertWebPush({
        scheduleId: definition.scheduleId,
        serviceKey: definition.serviceKey,
        title: definition.title,
        mode: 'change-threshold-volume-spike',
        thresholdPercent: definition.thresholdPercent,
        minVolumeIncreasePercent: definition.minVolumeIncreasePercent,
      });
    },
  }) as Promise<ManagedScheduleServiceResult>;
}

function trimmedRelativeDirectory(relativeDirectory: string) {
  return String(relativeDirectory ?? '').trim().replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
}
