import * as p from "@clack/prompts";
import path from "node:path";
import {
  ADAPTERS,
  checkTools,
  detectTargets,
  getAdapter,
  planInstall,
  tildify,
  type Scope,
  type TargetId,
} from "@designpaca/core";
import { getSkill } from "../skill.ts";
import { runInstall, printNextSteps, printOutcomes } from "../commands/install.ts";
import { accent, bold, dim } from "../ui.ts";

/** 선택한 범위를 어댑터가 지원하지 않으면 지원 가능한 범위로 낮춘다 */
function effectiveScope(target: TargetId, wanted: Scope): Scope {
  const a = getAdapter(target);
  return a.scopes.includes(wanted) ? wanted : (a.scopes[0] as Scope);
}

export async function onboard(): Promise<number> {
  const skill = await getSkill();

  console.clear();
  p.intro(`${accent("◆ designpaca")} ${dim(`v${skill.version}`)}`);

  p.note(
    [
      "웹 디자인을 순서대로 진행하게 만드는 스킬입니다.",
      "브리프 정리 → 레퍼런스 조사 → 방향 결정 → 디자인 토큰 →",
      "구현 → 셀프 감사까지, 단계마다 통과 조건을 두고 진행합니다.",
      "",
      dim("설치하면 에이전트에서 /designpaca 로 부를 수 있습니다."),
      dim("스킬 파일만 복사하며, 기존 설정은 건드리지 않습니다."),
    ].join("\n"),
    "designpaca 는 무엇인가요",
  );

  // 시스템에 흔적이 있는 도구를 기본 체크해 둔다 — 사용자가 매번 고르게 하지 않는다
  const detected = await detectTargets(skill);

  const targets = await p.multiselect<TargetId>({
    message: "어디에 설치할까요?",
    options: ADAPTERS.map((a) => ({
      value: a.id,
      label: a.label + (detected.includes(a.id) ? dim("  (감지됨)") : ""),
      hint: a.hint,
    })),
    initialValues: detected.length > 0 ? detected : ["claude-code"],
    required: true,
  });
  if (p.isCancel(targets)) return cancel();

  const wanted = await p.select<Scope>({
    message: "어느 범위에 설치할까요?",
    options: [
      {
        value: "user",
        label: "전역 (권장)",
        hint: "홈 디렉터리에 두어 모든 프로젝트에서 쓸 수 있습니다",
      },
      {
        value: "project",
        label: "이 프로젝트만",
        hint: `${tildify(process.cwd())} · 팀과 함께 쓰려면 이쪽이 좋습니다`,
      },
    ],
    initialValue: "user",
  });
  if (p.isCancel(wanted)) return cancel();

  // 범위가 낮춰진 타깃이 있으면 미리 알린다 — 설치 후에 "왜 여기 깔렸지"가 되지 않게
  const downgraded = targets.filter((t) => effectiveScope(t, wanted) !== wanted);
  if (downgraded.length > 0) {
    p.log.warn(
      downgraded
        .map((t) => {
          const a = getAdapter(t);
          return `${a.label} 은(는) ${a.scopes.join("/")} 범위만 지원해서, ${effectiveScope(t, wanted)} 범위로 설치됩니다`;
        })
        .join("\n"),
    );
  }

  // 무엇이 어디에 쓰이는지 먼저 보여준다
  const previews: string[] = [];
  for (const t of targets) {
    const scope = effectiveScope(t, wanted);
    const plan = await planInstall(t, scope, skill);
    if (plan.blocked) {
      previews.push(`${bold(getAdapter(t).label)}\n  ${dim(plan.blocked)}`);
      continue;
    }
    const dirs = new Set(plan.actions.map((a) => path.dirname(a.path)));
    previews.push(
      [
        `${bold(getAdapter(t).label)} ${plan.alreadyInstalled ? dim("(재설치)") : ""}`,
        `  ${dim(tildify(plan.root))}`,
        `  ${dim(`파일 ${plan.actions.length}개 · 디렉터리 ${dirs.size}개`)}`,
      ].join("\n"),
    );
  }
  p.note(
    [
      previews.join("\n\n"),
      "",
      dim("아래 경로에 스킬 파일을 복사합니다."),
      dim("직접 수정하신 파일이 있으면 덮어쓰지 않고 알려드립니다."),
    ].join("\n"),
    "이렇게 설치됩니다",
  );

  const go = await p.confirm({ message: "이대로 진행할까요?", initialValue: true });
  if (p.isCancel(go) || !go) return cancel();

  const s = p.spinner();
  s.start("설치하고 있습니다");
  const outcomes = [];
  try {
    for (const t of targets) {
      const scope = effectiveScope(t, wanted);
      s.message(`${getAdapter(t).label} 에 설치하고 있습니다`);
      outcomes.push(...(await runInstall({ targets: [t], scope })));
    }
    s.stop("설치가 끝났습니다");
  } catch (err) {
    s.stop("설치하지 못했습니다", 1);
    p.log.error(err instanceof Error ? err.message : String(err));
    return 1;
  }

  printOutcomes(outcomes);

  await offerTools();

  printNextSteps(targets);

  p.outro(`${accent("designpaca")} 준비됐습니다 — 만들고 싶은 것을 말씀해 주세요`);
  return 0;
}

/**
 * 선택 도구를 제안한다. 스킬 설치가 끝난 뒤에 묻는다 —
 * 본 작업을 이것 때문에 붙잡지 않기 위해서다. 건너뛰어도 스킬은 돈다.
 */
export async function offerTools(): Promise<void> {
  const status = await checkTools();
  const missing = status.filter((s) => !s.installed);

  if (missing.length === 0) {
    p.log.success(
      `브라우저 도구가 이미 준비돼 있습니다 ${dim("— 레퍼런스 조사에서 바로 활용됩니다")}`,
    );
    return;
  }

  p.note(
    [
      "이 스킬은 디자인을 시작하기 전에 실제 사이트를 찾아 분석합니다.",
      "그런데 참고할 만한 사이트일수록 자동 접근을 막아두는 경우가 많습니다.",
      "",
      "브라우저 도구가 있으면 그런 사이트도 직접 열어서",
      "화면을 캡처하고 글자 크기·여백 같은 값을 실제로 잴 수 있습니다.",
      "",
      dim("설치하지 않아도 스킬은 정상 동작합니다."),
      dim("나중에 `designpaca tools` 로 언제든 추가하실 수 있습니다."),
    ].join("\n"),
    "브라우저 도구를 함께 설치할까요?",
  );

  const picked = await p.multiselect<string>({
    message: "설치할 도구를 골라주세요 (스페이스로 선택 / 엔터로 확인)",
    options: missing.map(({ tool }) => ({
      value: tool.id,
      label: tool.label,
      hint: `${tool.why} · ${tool.size}`,
    })),
    initialValues: missing.map((m) => m.tool.id),
    required: false,
  });
  if (p.isCancel(picked) || picked.length === 0) {
    p.log.info(
      `건너뛰겠습니다. ${dim("필요해지면 `designpaca tools` 로 설치하실 수 있습니다.")}`,
    );
    return;
  }

  const s = p.spinner();
  const followUps: string[] = [];
  for (const id of picked) {
    const entry = missing.find((m) => m.tool.id === id);
    if (!entry) continue;
    s.start(`${entry.tool.label} 을(를) 설치하고 있습니다 ${dim(`(${entry.tool.size})`)}`);
    try {
      await entry.tool.install();
      s.stop(`${entry.tool.label} 설치 완료`);
      if (entry.tool.followUp) followUps.push(entry.tool.followUp);
    } catch (err) {
      // 도구 설치 실패로 스킬 설치까지 실패시키지 않는다. 스킬은 이미 깔렸다.
      s.stop(`${entry.tool.label} 을(를) 설치하지 못했습니다`, 1);
      p.log.warn(
        [
          err instanceof Error ? err.message.split("\n")[0] : String(err),
          dim("스킬 설치는 정상적으로 끝났습니다. 이 도구만 나중에 다시 시도하셔도 됩니다."),
        ].join("\n"),
      );
    }
  }

  if (followUps.length > 0) {
    p.note(
      [
        "아래 명령은 에이전트 설정을 바꾸는 것이라 직접 실행해 주세요.",
        "",
        ...followUps,
      ].join("\n"),
      "마지막 한 단계",
    );
  }
}

function cancel(): number {
  p.cancel("설치를 취소했습니다. 아무것도 변경하지 않았습니다.");
  return 130;
}
