import type { StateStore } from "./stateStore";
import type { YouTrackClient } from "./youtrackClient";
import type { HetznerStatus } from "./hetznerClient";
import type { SentryOutageIssue } from "./sentryClient";
import { createTicketForOutage } from "./handlers";
import { beginRecovery, runDueRecoveries } from "./recovery";

export interface PollDeps {
  store: StateStore;
  yt: Pick<YouTrackClient, "createIssue" | "setAssignee" | "setState" | "addComment" | "getState">;
  fetchOutages: () => Promise<SentryOutageIssue[]>;
  hetzner: (feed: string, regions: string[]) => Promise<HetznerStatus>;
  siteFetch: (url: string) => Promise<number>;
  discord: (content: string) => Promise<void>;
  env: Env;
  now: () => number;
}

function regions(env: Env): string[] {
  return env.HETZNER_REGIONS.split(",").map((s) => s.trim()).filter(Boolean);
}

/**
 * One cron cycle. Poll Sentry for outage-category issues, then reconcile:
 *  1. new unresolved outage  → open a PA ticket (dedup by issue id)
 *  2. tracked open outage now resolved in Sentry → begin recovery verification
 *  3. run due recovery checks (t=0/+1/+5) → In Review / escalate + Discord
 * Hetzner status is fetched at most once per cycle (lazily, only when needed).
 */
export async function pollOutages(d: PollDeps): Promise<void> {
  const issues = await d.fetchOutages();
  const byId = new Map(issues.map((i) => [i.id, i]));

  let hzCache: HetznerStatus | null = null;
  const hetzner = async (): Promise<HetznerStatus> => {
    if (!hzCache) hzCache = await d.hetzner(d.env.HETZNER_FEED, regions(d.env));
    return hzCache;
  };

  // 1. New downtime → ticket (createTicketForOutage dedups on the issue id).
  for (const issue of issues) {
    if (issue.status !== "unresolved") continue;
    await createTicketForOutage(issue, { store: d.store, yt: d.yt, hetzner, env: d.env, now: d.now });
  }

  // 2. Tracked open outages: either resolved (→ recovery) or still down (→ keep
  //    re-checking Hetzner; the incident is often posted minutes after onset).
  for (const rec of await d.store.listOpen()) {
    const issue = byId.get(rec.issueId);
    if (issue && issue.status === "resolved") {
      await beginRecovery(rec, d.store, d.now());
      continue;
    }
    // Still down and no Hetzner correlation yet → re-check the feed.
    if (!rec.hetznerMatched) {
      const match = (await hetzner()).activeIncidents.find((i) => i.matchedRegion);
      if (match && rec.ticketId) {
        await d.yt.addComment(
          rec.ticketId,
          `🔶 Hetzner incident raporlandı (kesinti sürerken): ${match.title} (${match.link})`,
        );
        rec.hetznerMatched = true;
        await d.store.put(rec);
      }
    }
  }

  // 3. Run due recovery checks.
  const verifying = await d.store.listVerifying();
  if (verifying.length > 0) {
    const summary = (await hetzner()).summary;
    await runDueRecoveries({
      store: d.store,
      yt: d.yt,
      siteFetch: d.siteFetch,
      discord: d.discord,
      env: d.env,
      now: d.now,
      hetznerSummary: summary,
    });
  }
}
