import type { Logger } from "pino";
import { OBSERVABILITY, POLLER, TICKET_REVIEW_COMMENT_TRIGGER } from "@/core/constants.js";
import { METRIC } from "@/metrics/names.js";
import type { Metrics } from "@/metrics/types.js";
import type { JiraClientImpl } from "@/jira/client.js";
import type { IJobQueue } from "@/types/index.js";
import { PollRunner } from "@/poller/poll-runner.js";
import {
  isTicketReviewCommentEligibleBySessionStart,
  isTicketReviewTriggerProcessed,
  ticketReviewPersistenceFileExists,
  ticketReviewTriggerKey,
} from "@/poller/ticket-review-processed.js";

function commentIncludesTicketReviewTrigger(body: string): boolean {
  return body.toLowerCase().includes(TICKET_REVIEW_COMMENT_TRIGGER);
}

interface PollerDeps {
  jiraClient: JiraClientImpl;
  queue: IJobQueue;
  metrics: Metrics;
  botEmail: string;
  squadScope: string[];
  logger: Logger;
}

/**
 * Polls Jira for issues whose comments contain `review-ticket` and an @mention of the bot,
 * then enqueues {@link JiraTicketReviewJobPayload}.
 */
export class JiraTicketReviewPoller {
  private timer: ReturnType<typeof setInterval> | null = null;
  private botAccountId: string | null = null;
  private readonly runner: PollRunner;

  constructor(private readonly deps: PollerDeps) {
    this.runner = new PollRunner({
      poller: "jira_ticket_review",
      queue: deps.queue,
      metrics: deps.metrics,
      logger: deps.logger,
      contendingKinds: ["jira", "pr_comment"],
      skipLogThrottleMs: OBSERVABILITY.SKIP_LOG_THROTTLE_MS,
    });
  }

  start(): void {
    if (this.timer) return;

    this.deps.logger.info(
      { botEmail: this.deps.botEmail, intervalMs: POLLER.TICKET_REVIEW_INTERVAL_MS },
      "ticket_review_poller_started",
    );

    void this.poll();
    this.timer = setInterval(() => void this.poll(), POLLER.TICKET_REVIEW_INTERVAL_MS);
  }

  stop(): void {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = null;
    }
    this.deps.logger.info("ticket_review_poller_stopped");
  }

  async poll(): Promise<number> {
    return this.runner.run(async () => {
      if (!this.botAccountId) {
        const accountId = await this.deps.jiraClient.resolveAccountIdForBotEmail(
          this.deps.botEmail,
        );
        if (!accountId) {
          this.deps.metrics.increment(METRIC.pollAuthFailure, {
            poller: "jira_ticket_review",
            integration: "jira",
          });
          this.deps.logger.error(
            { botEmail: this.deps.botEmail },
            "ticket_review_poller_no_bot_account_id",
          );
          return 0;
        }
        this.botAccountId = accountId;
      }

      const projectFilter =
        this.deps.squadScope.length > 0
          ? `project in (${this.deps.squadScope.join(", ")}) AND `
          : "";
      const jql = `${projectFilter}comment ~ "${TICKET_REVIEW_COMMENT_TRIGGER}" ORDER BY updated DESC`;

      let result: Awaited<ReturnType<JiraClientImpl["searchIssuesLite"]>>;
      try {
        result = await this.deps.jiraClient.searchIssuesLite(jql, 40);
      } catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        if (/\b401\b|\b403\b/.test(msg)) {
          this.botAccountId = null;
          this.deps.metrics.increment(METRIC.pollAuthFailure, {
            poller: "jira_ticket_review",
            integration: "jira",
          });
        }
        throw err;
      }

      let enqueued = 0;
      for (const issue of result.issues) {
        const mentionToken = `[mention:${this.botAccountId}]`;

        const comments = [...issue.comments].sort(
          (a, b) => new Date(b.created).getTime() - new Date(a.created).getTime(),
        );

        for (const c of comments) {
          if (c.author.accountId === this.botAccountId) {
            continue;
          }
          if (!commentIncludesTicketReviewTrigger(c.body)) {
            continue;
          }
          if (!c.body.includes(mentionToken)) {
            continue;
          }

          if (
            !ticketReviewPersistenceFileExists() &&
            !isTicketReviewCommentEligibleBySessionStart(c.created)
          ) {
            this.deps.logger
              .child({ issueKey: issue.key })
              .debug(
                { commentId: c.id, created: c.created },
                "ticket_review_skipped_comment_before_session_no_store_yet",
              );
            continue;
          }

          const key = ticketReviewTriggerKey(issue.key, c.id);
          if (isTicketReviewTriggerProcessed(issue.key, c.id)) {
            continue;
          }

          await this.deps.queue.enqueue({
            jobKind: "jira_ticket_review",
            issueId: issue.id,
            issueKey: issue.key,
            summary: issue.summary,
            triggeredByCommentId: c.id,
          });
          this.deps.logger
            .child({ issueKey: issue.key })
            .info({ commentId: c.id, dedupeKey: key }, "ticket_review_job_enqueued");
          enqueued++;
          break;
        }
      }

      return enqueued;
    });
  }
}
