const dayjs = require("dayjs");

class TaskRunner {
  constructor(store, telegram, task) {
    this.store = store;
    this.telegram = telegram;
    this.task = task;
    this.running = false;
    this.timer = null;
  }

  isRunning() {
    return this.running;
  }

  async start() {
    if (this.running) return;
    this.running = true;
    await this._initMonitoring();
    this._scheduleNext();
  }

  stop() {
    this.running = false;
    if (this.timer) clearTimeout(this.timer);
    this.timer = null;
    this.telegram.stopTaskMonitor(this.task.id);
  }

  async _initMonitoring() {
    const competitors = this.store.listTaskCompetitors(this.task.id).map((row) => row.link);
    const accounts = this.store.listTaskAccounts(this.task.id).map((row) => row.account_id);
    await this.telegram.joinGroupsForTask(this.task, competitors, accounts);
    await this.telegram.startTaskMonitor(this.task, competitors, accounts);
  }

  _scheduleNext() {
    if (!this.running) return;
    const minMs = Number(this.task.min_interval_minutes || 5) * 60 * 1000;
    const maxMs = Number(this.task.max_interval_minutes || 10) * 60 * 1000;
    const jitter = Math.max(minMs, Math.min(maxMs, minMs + Math.random() * (maxMs - minMs)));
    this.timer = setTimeout(() => this._runBatch(), jitter);
  }

  async _runBatch() {
    const startedAt = dayjs().toISOString();
    const errors = [];
    const successIds = [];
    let invitedCount = 0;

    try {
      const accounts = this.store.listTaskAccounts(this.task.id).map((row) => row.account_id);
      if (!accounts.length) {
        errors.push("No accounts assigned");
      }
      let inviteAccounts = accounts;
      if (!this.task.multi_accounts_per_run) {
        const entry = this.telegram.pickInviteAccount(inviteAccounts, Boolean(this.task.random_accounts));
        inviteAccounts = entry ? [entry.account.id] : [];
      }
      const totalAccounts = accounts.length;
      if (this.task.stop_on_blocked) {
        const all = this.store.listAccounts().filter((acc) => accounts.includes(acc.id));
        const blocked = all.filter((acc) => acc.status !== "ok").length;
        const percent = totalAccounts ? Math.round((blocked / totalAccounts) * 100) : 0;
        if (percent >= Number(this.task.stop_blocked_percent || 25)) {
          errors.push(`Stopped: blocked ${percent}% >= ${this.task.stop_blocked_percent}%`);
          this.stop();
        }
      }

      const dailyLimit = Number(this.task.daily_limit || 100);
      const alreadyInvited = this.store.countInvitesToday(this.task.id);
      if (alreadyInvited >= dailyLimit) {
        errors.push("Daily limit reached");
      } else {
        const remaining = dailyLimit - alreadyInvited;
        const batchSize = Math.min(20, remaining);
        const pending = this.store.getPendingInvites(this.task.id, batchSize);
        if (!inviteAccounts.length && pending.length) {
          errors.push("No available accounts under limits");
        }

        for (const item of pending) {
          if (item.attempts >= 2 && this.task.retry_on_fail) {
            this.store.markInviteStatus(item.id, "failed");
            continue;
          }
          const result = await this.telegram.inviteUserForTask(this.task, item.user_id, inviteAccounts, {
            randomize: Boolean(this.task.random_accounts),
            userAccessHash: item.user_access_hash,
            username: item.username
          });
          if (result.ok) {
            invitedCount += 1;
            successIds.push(item.user_id);
            this.store.markInviteStatus(item.id, "invited");
            this.store.recordInvite(
              this.task.id,
              item.user_id,
              item.username,
              result.accountId,
              result.accountPhone,
              item.source_chat,
              "success",
              "",
              "",
              "invite"
            );
          } else {
            errors.push(`${item.user_id}: ${result.error}`);
            if (this.task.retry_on_fail) {
              this.store.incrementInviteAttempt(item.id);
              this.store.markInviteStatus(item.id, "pending");
            } else {
              this.store.markInviteStatus(item.id, "failed");
            }
            this.store.recordInvite(
              this.task.id,
              item.user_id,
              item.username,
              result.accountId,
              result.accountPhone,
              item.source_chat,
              "failed",
              result.error || "",
              result.error || "",
              "invite"
            );
          }
        }
      }
    } catch (error) {
      errors.push(error.message || String(error));
    }

    const finishedAt = dayjs().toISOString();
    this.store.addLog({
      taskId: this.task.id,
      startedAt,
      finishedAt,
      invitedCount,
      successIds,
      errors
    });

    this._scheduleNext();
  }
}

module.exports = { TaskRunner };
