const fs = require("fs");
const path = require("path");
const Database = require("better-sqlite3");
const dayjs = require("dayjs");

const DEFAULT_SETTINGS = {
  competitorGroups: [""],
  ourGroup: "",
  minIntervalMinutes: 5,
  maxIntervalMinutes: 10,
  dailyLimit: 100,
  historyLimit: 200,
  accountMaxGroups: 10,
  accountDailyLimit: 50,
  floodCooldownMinutes: 1440,
  autoJoinCompetitors: false,
  autoJoinOurGroup: false
};

function initStore(userDataPath) {
  const dataDir = path.join(userDataPath, "data");
  if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true });
  const dbPath = path.join(dataDir, "app.db");
  const db = new Database(dbPath);

  db.pragma("journal_mode = WAL");

  db.exec(`
    CREATE TABLE IF NOT EXISTS settings (
      key TEXT PRIMARY KEY,
      value TEXT NOT NULL
    );

    CREATE TABLE IF NOT EXISTS accounts (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      phone TEXT NOT NULL,
      api_id INTEGER NOT NULL,
      api_hash TEXT NOT NULL,
      session TEXT NOT NULL,
      user_id TEXT DEFAULT '',
      max_groups INTEGER DEFAULT 10,
      daily_limit INTEGER DEFAULT 50,
      status TEXT NOT NULL DEFAULT 'ok',
      last_error TEXT DEFAULT '',
      cooldown_until TEXT DEFAULT '',
      cooldown_reason TEXT DEFAULT '',
      created_at TEXT NOT NULL,
      updated_at TEXT NOT NULL
    );

    CREATE TABLE IF NOT EXISTS invite_queue (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      task_id INTEGER DEFAULT 0,
      user_id TEXT NOT NULL,
      username TEXT DEFAULT '',
      user_access_hash TEXT DEFAULT '',
      source_chat TEXT NOT NULL,
      attempts INTEGER NOT NULL DEFAULT 0,
      status TEXT NOT NULL DEFAULT 'pending',
      created_at TEXT NOT NULL,
      updated_at TEXT NOT NULL,
      UNIQUE(user_id, source_chat)
    );

    CREATE TABLE IF NOT EXISTS logs (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      task_id INTEGER DEFAULT 0,
      started_at TEXT NOT NULL,
      finished_at TEXT NOT NULL,
      invited_count INTEGER NOT NULL,
      success_ids TEXT NOT NULL,
      error_summary TEXT NOT NULL
    );

    CREATE TABLE IF NOT EXISTS account_events (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      account_id INTEGER NOT NULL,
      phone TEXT NOT NULL,
      event_type TEXT NOT NULL,
      message TEXT NOT NULL,
      created_at TEXT NOT NULL
    );

    CREATE TABLE IF NOT EXISTS invites (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      task_id INTEGER DEFAULT 0,
      user_id TEXT NOT NULL,
      username TEXT DEFAULT '',
      user_access_hash TEXT DEFAULT '',
      account_id INTEGER DEFAULT 0,
      account_phone TEXT DEFAULT '',
      source_chat TEXT DEFAULT '',
      action TEXT DEFAULT 'invite',
      skipped_reason TEXT DEFAULT '',
      invited_at TEXT NOT NULL,
      status TEXT NOT NULL,
      error TEXT NOT NULL
    );

    CREATE TABLE IF NOT EXISTS tasks (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      name TEXT NOT NULL,
      our_group TEXT NOT NULL,
      min_interval_minutes INTEGER NOT NULL,
      max_interval_minutes INTEGER NOT NULL,
      daily_limit INTEGER NOT NULL,
      history_limit INTEGER NOT NULL,
      max_competitor_bots INTEGER NOT NULL,
      max_our_bots INTEGER NOT NULL,
      random_accounts INTEGER NOT NULL DEFAULT 0,
      multi_accounts_per_run INTEGER NOT NULL DEFAULT 0,
      retry_on_fail INTEGER NOT NULL DEFAULT 0,
      auto_join_competitors INTEGER NOT NULL DEFAULT 1,
      auto_join_our_group INTEGER NOT NULL DEFAULT 1,
      stop_on_blocked INTEGER NOT NULL DEFAULT 0,
      stop_blocked_percent INTEGER NOT NULL DEFAULT 25,
      notes TEXT NOT NULL DEFAULT '',
      enabled INTEGER NOT NULL DEFAULT 1,
      created_at TEXT NOT NULL,
      updated_at TEXT NOT NULL
    );

    CREATE TABLE IF NOT EXISTS task_competitors (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      task_id INTEGER NOT NULL,
      link TEXT NOT NULL
    );

    CREATE TABLE IF NOT EXISTS task_accounts (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      task_id INTEGER NOT NULL,
      account_id INTEGER NOT NULL
    );
  `);

  const ensureColumn = (table, column, definition) => {
    const columns = db.prepare(`PRAGMA table_info(${table})`).all();
    const exists = columns.some((col) => col.name === column);
    if (!exists) {
      db.prepare(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`).run();
    }
  };

  ensureColumn("invite_queue", "username", "TEXT DEFAULT ''");
  ensureColumn("invite_queue", "user_access_hash", "TEXT DEFAULT ''");
  ensureColumn("invites", "username", "TEXT DEFAULT ''");
  ensureColumn("invites", "user_access_hash", "TEXT DEFAULT ''");
  ensureColumn("invites", "account_id", "INTEGER DEFAULT 0");
  ensureColumn("accounts", "max_groups", "INTEGER DEFAULT 10");
  ensureColumn("accounts", "daily_limit", "INTEGER DEFAULT 50");
  ensureColumn("invites", "account_phone", "TEXT DEFAULT ''");
  ensureColumn("invites", "source_chat", "TEXT DEFAULT ''");
  ensureColumn("invites", "action", "TEXT DEFAULT 'invite'");
  ensureColumn("invites", "skipped_reason", "TEXT DEFAULT ''");
  ensureColumn("accounts", "cooldown_until", "TEXT DEFAULT ''");
  ensureColumn("accounts", "cooldown_reason", "TEXT DEFAULT ''");
  ensureColumn("accounts", "user_id", "TEXT DEFAULT ''");
  ensureColumn("invite_queue", "task_id", "INTEGER DEFAULT 0");
  ensureColumn("invite_queue", "attempts", "INTEGER NOT NULL DEFAULT 0");
  ensureColumn("logs", "task_id", "INTEGER DEFAULT 0");
  ensureColumn("invites", "task_id", "INTEGER DEFAULT 0");
  ensureColumn("tasks", "auto_join_competitors", "INTEGER NOT NULL DEFAULT 1");
  ensureColumn("tasks", "auto_join_our_group", "INTEGER NOT NULL DEFAULT 1");

  const settingsRow = db.prepare("SELECT value FROM settings WHERE key = ?").get("settings");
  if (!settingsRow) {
    db.prepare("INSERT INTO settings (key, value) VALUES (?, ?)")
      .run("settings", JSON.stringify(DEFAULT_SETTINGS));
  }

  function getSettings() {
    const row = db.prepare("SELECT value FROM settings WHERE key = ?").get("settings");
    if (!row) return { ...DEFAULT_SETTINGS };
    try {
      const parsed = JSON.parse(row.value);
      const normalized = { ...DEFAULT_SETTINGS, ...parsed };
      if (typeof normalized.competitorGroup === "string" && normalized.competitorGroups == null) {
        normalized.competitorGroups = [normalized.competitorGroup];
      }
      if (!Array.isArray(normalized.competitorGroups) || normalized.competitorGroups.length === 0) {
        normalized.competitorGroups = [""];
      }
      return normalized;
    } catch (error) {
      return { ...DEFAULT_SETTINGS };
    }
  }

  function saveSettings(settings) {
    const payload = { ...DEFAULT_SETTINGS, ...settings };
    if (typeof payload.competitorGroup === "string" && payload.competitorGroups == null) {
      payload.competitorGroups = [payload.competitorGroup];
    }
    if (!Array.isArray(payload.competitorGroups) || payload.competitorGroups.length === 0) {
      payload.competitorGroups = [""];
    }
    db.prepare("UPDATE settings SET value = ? WHERE key = ?")
      .run(JSON.stringify(payload), "settings");
    return payload;
  }

  function listAccounts() {
    return db.prepare("SELECT * FROM accounts ORDER BY id DESC").all();
  }

  function clearAllData() {
    db.prepare("DELETE FROM task_accounts").run();
    db.prepare("DELETE FROM task_competitors").run();
    db.prepare("DELETE FROM tasks").run();
    db.prepare("DELETE FROM invite_queue").run();
    db.prepare("DELETE FROM invites").run();
    db.prepare("DELETE FROM logs").run();
    db.prepare("DELETE FROM account_events").run();
    db.prepare("DELETE FROM accounts").run();
    db.prepare("DELETE FROM settings").run();
    db.prepare("INSERT INTO settings (key, value) VALUES (?, ?)")
      .run("settings", JSON.stringify(DEFAULT_SETTINGS));
  }

  function findAccountByIdentity({ userId, phone, session }) {
    return db.prepare(`
      SELECT * FROM accounts
      WHERE (user_id = ? AND user_id != '')
         OR (phone = ? AND phone != '')
         OR (session = ? AND session != '')
      LIMIT 1
    `).get(userId || "", phone || "", session || "");
  }

  function addAccount(account) {
    const now = dayjs().toISOString();
    const result = db.prepare(`
      INSERT INTO accounts (phone, api_id, api_hash, session, user_id, max_groups, daily_limit, status, last_error, created_at, updated_at)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    `).run(
      account.phone,
      account.apiId,
      account.apiHash,
      account.session,
      account.userId || "",
      account.maxGroups ?? DEFAULT_SETTINGS.accountMaxGroups,
      account.dailyLimit ?? DEFAULT_SETTINGS.accountDailyLimit,
      account.status || "ok",
      account.lastError || "",
      now,
      now
    );
    return result.lastInsertRowid;
  }

  function updateAccountStatus(id, status, lastError) {
    const now = dayjs().toISOString();
    db.prepare("UPDATE accounts SET status = ?, last_error = ?, updated_at = ? WHERE id = ?")
      .run(status, lastError || "", now, id);
  }

  function updateAccountIdentity(id, userId, phone) {
    const now = dayjs().toISOString();
    db.prepare("UPDATE accounts SET user_id = ?, phone = ?, updated_at = ? WHERE id = ?")
      .run(userId || "", phone || "", now, id);
  }

  function setAccountCooldown(id, minutes, reason) {
    const now = dayjs();
    const until = minutes > 0 ? now.add(minutes, "minute").toISOString() : "";
    const status = "limited";
    db.prepare(`
      UPDATE accounts
      SET status = ?, last_error = ?, cooldown_until = ?, cooldown_reason = ?, updated_at = ?
      WHERE id = ?
    `).run(status, reason || "", until, reason || "", now.toISOString(), id);
  }

  function clearAccountCooldown(id) {
    const now = dayjs().toISOString();
    db.prepare(`
      UPDATE accounts
      SET status = 'ok', last_error = '', cooldown_until = '', cooldown_reason = '', updated_at = ?
      WHERE id = ?
    `).run(now, id);
  }

  function deleteAccount(id) {
    db.prepare("DELETE FROM accounts WHERE id = ?").run(id);
    db.prepare("DELETE FROM task_accounts WHERE account_id = ?").run(id);
  }

  function addAccountEvent(accountId, phone, eventType, message) {
    const now = dayjs().toISOString();
    db.prepare(`
      INSERT INTO account_events (account_id, phone, event_type, message, created_at)
      VALUES (?, ?, ?, ?, ?)
    `).run(accountId, phone || "", eventType, message || "", now);
  }

  function listAccountEvents(limit) {
    const rows = db.prepare(`
      SELECT * FROM account_events
      ORDER BY id DESC
      LIMIT ?
    `).all(limit || 200);
    return rows.map((row) => ({
      id: row.id,
      accountId: row.account_id,
      phone: row.phone,
      eventType: row.event_type,
      message: row.message,
      createdAt: row.created_at
    }));
  }

  function enqueueInvite(taskId, userId, username, sourceChat, accessHash) {
    const now = dayjs().toISOString();
    try {
      db.prepare(`
        INSERT INTO invite_queue (task_id, user_id, username, user_access_hash, source_chat, status, created_at, updated_at)
        VALUES (?, ?, ?, ?, ?, 'pending', ?, ?)
      `).run(taskId || 0, userId, username || "", accessHash || "", sourceChat, now, now);
      return true;
    } catch (error) {
      return false;
    }
  }

  function getPendingInvites(taskId, limit) {
    return db.prepare(`
      SELECT * FROM invite_queue
      WHERE status = 'pending' AND task_id = ?
      ORDER BY id ASC
      LIMIT ?
    `).all(taskId || 0, limit);
  }

  function getPendingCount(taskId) {
    if (taskId == null) {
      return db.prepare("SELECT COUNT(*) as count FROM invite_queue WHERE status = 'pending'").get().count;
    }
    return db.prepare("SELECT COUNT(*) as count FROM invite_queue WHERE status = 'pending' AND task_id = ?")
      .get(taskId || 0).count;
  }

  function clearQueue(taskId) {
    if (taskId == null) {
      db.prepare("DELETE FROM invite_queue").run();
      return;
    }
    db.prepare("DELETE FROM invite_queue WHERE task_id = ?").run(taskId || 0);
  }

  function listTasks() {
    return db.prepare("SELECT * FROM tasks ORDER BY id DESC").all();
  }

  function getTask(id) {
    return db.prepare("SELECT * FROM tasks WHERE id = ?").get(id);
  }

  function saveTask(task) {
    const now = dayjs().toISOString();
    if (task.id) {
      db.prepare(`
        UPDATE tasks
        SET name = ?, our_group = ?, min_interval_minutes = ?, max_interval_minutes = ?, daily_limit = ?,
            history_limit = ?, max_competitor_bots = ?, max_our_bots = ?, random_accounts = ?, multi_accounts_per_run = ?,
            retry_on_fail = ?, auto_join_competitors = ?, auto_join_our_group = ?, stop_on_blocked = ?, stop_blocked_percent = ?, notes = ?, enabled = ?, updated_at = ?
        WHERE id = ?
      `).run(
        task.name,
        task.ourGroup,
        task.minIntervalMinutes,
        task.maxIntervalMinutes,
        task.dailyLimit,
        task.historyLimit,
        task.maxCompetitorBots,
        task.maxOurBots,
        task.randomAccounts ? 1 : 0,
        task.multiAccountsPerRun ? 1 : 0,
        task.retryOnFail ? 1 : 0,
        task.autoJoinCompetitors ? 1 : 0,
        task.autoJoinOurGroup ? 1 : 0,
        task.stopOnBlocked ? 1 : 0,
        task.stopBlockedPercent || 25,
        task.notes || "",
        task.enabled ? 1 : 0,
        now,
        task.id
      );
      return task.id;
    }

    const result = db.prepare(`
      INSERT INTO tasks (name, our_group, min_interval_minutes, max_interval_minutes, daily_limit, history_limit,
        max_competitor_bots, max_our_bots, random_accounts, multi_accounts_per_run, retry_on_fail, auto_join_competitors,
        auto_join_our_group, stop_on_blocked, stop_blocked_percent, notes, enabled, created_at, updated_at)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    `).run(
      task.name,
      task.ourGroup,
      task.minIntervalMinutes,
      task.maxIntervalMinutes,
      task.dailyLimit,
      task.historyLimit,
      task.maxCompetitorBots,
      task.maxOurBots,
      task.randomAccounts ? 1 : 0,
      task.multiAccountsPerRun ? 1 : 0,
      task.retryOnFail ? 1 : 0,
      task.autoJoinCompetitors ? 1 : 0,
      task.autoJoinOurGroup ? 1 : 0,
      task.stopOnBlocked ? 1 : 0,
      task.stopBlockedPercent || 25,
      task.notes || "",
      task.enabled ? 1 : 0,
      now,
      now
    );
    return result.lastInsertRowid;
  }

  function deleteTask(id) {
    db.prepare("DELETE FROM tasks WHERE id = ?").run(id);
    db.prepare("DELETE FROM task_competitors WHERE task_id = ?").run(id);
    db.prepare("DELETE FROM task_accounts WHERE task_id = ?").run(id);
  }

  function listTaskCompetitors(taskId) {
    return db.prepare("SELECT * FROM task_competitors WHERE task_id = ? ORDER BY id ASC").all(taskId);
  }

  function setTaskCompetitors(taskId, links) {
    db.prepare("DELETE FROM task_competitors WHERE task_id = ?").run(taskId);
    const stmt = db.prepare("INSERT INTO task_competitors (task_id, link) VALUES (?, ?)");
    (links || []).filter(Boolean).forEach((link) => stmt.run(taskId, link));
  }

  function listTaskAccounts(taskId) {
    return db.prepare("SELECT * FROM task_accounts WHERE task_id = ?").all(taskId);
  }

  function listAllTaskAccounts() {
    return db.prepare("SELECT * FROM task_accounts").all();
  }

  function setTaskAccounts(taskId, accountIds) {
    db.prepare("DELETE FROM task_accounts WHERE task_id = ?").run(taskId);
    const stmt = db.prepare("INSERT INTO task_accounts (task_id, account_id) VALUES (?, ?)");
    (accountIds || []).forEach((accountId) => stmt.run(taskId, accountId));
  }

  function markInviteStatus(queueId, status) {
    const now = dayjs().toISOString();
    db.prepare("UPDATE invite_queue SET status = ?, updated_at = ? WHERE id = ?")
      .run(status, now, queueId);
  }

  function incrementInviteAttempt(queueId) {
    const now = dayjs().toISOString();
    db.prepare("UPDATE invite_queue SET attempts = attempts + 1, updated_at = ? WHERE id = ?")
      .run(now, queueId);
  }

  function recordInvite(taskId, userId, username, accountId, accountPhone, sourceChat, status, error, skippedReason, action) {
    const now = dayjs().toISOString();
    db.prepare(`
      INSERT INTO invites (task_id, user_id, username, account_id, account_phone, source_chat, action, skipped_reason, invited_at, status, error)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    `).run(
      taskId || 0,
      userId,
      username || "",
      accountId || 0,
      accountPhone || "",
      sourceChat || "",
      action || "invite",
      skippedReason || "",
      now,
      status,
      error || ""
    );
  }

  function countInvitesToday(taskId) {
    const dayStart = dayjs().startOf("day").toISOString();
    if (taskId == null) {
      return db.prepare(
        "SELECT COUNT(*) as count FROM invites WHERE invited_at >= ? AND status = 'success'"
      ).get(dayStart).count;
    }
    return db.prepare(
      "SELECT COUNT(*) as count FROM invites WHERE invited_at >= ? AND status = 'success' AND task_id = ?"
    ).get(dayStart, taskId || 0).count;
  }

  function countInvitesTodayByAccount(accountId, taskId) {
    const dayStart = dayjs().startOf("day").toISOString();
    if (taskId == null) {
      return db.prepare(
        "SELECT COUNT(*) as count FROM invites WHERE invited_at >= ? AND status = 'success' AND account_id = ?"
      ).get(dayStart, accountId).count;
    }
    return db.prepare(
      "SELECT COUNT(*) as count FROM invites WHERE invited_at >= ? AND status = 'success' AND account_id = ? AND task_id = ?"
    ).get(dayStart, accountId, taskId || 0).count;
  }

  function addLog(entry) {
    db.prepare(`
      INSERT INTO logs (task_id, started_at, finished_at, invited_count, success_ids, error_summary)
      VALUES (?, ?, ?, ?, ?, ?)
    `).run(
      entry.taskId || 0,
      entry.startedAt,
      entry.finishedAt,
      entry.invitedCount,
      JSON.stringify(entry.successIds || []),
      JSON.stringify(entry.errors || [])
    );
  }

  function listLogs(limit, taskId) {
    let rows = [];
    if (taskId != null) {
      rows = db.prepare("SELECT * FROM logs WHERE task_id = ? ORDER BY id DESC LIMIT ?").all(taskId || 0, limit || 100);
    } else {
      rows = db.prepare("SELECT * FROM logs ORDER BY id DESC LIMIT ?").all(limit || 100);
    }
    return rows.map((row) => ({
      id: row.id,
      taskId: row.task_id || 0,
      startedAt: row.started_at,
      finishedAt: row.finished_at,
      invitedCount: row.invited_count,
      successIds: JSON.parse(row.success_ids || "[]"),
      errors: JSON.parse(row.error_summary || "[]")
    }));
  }

  function clearLogs(taskId) {
    if (taskId == null) {
      db.prepare("DELETE FROM logs").run();
      return;
    }
    db.prepare("DELETE FROM logs WHERE task_id = ?").run(taskId || 0);
  }

  function listInvites(limit, taskId) {
    let rows = [];
    if (taskId != null) {
      rows = db.prepare(`
        SELECT * FROM invites
        WHERE task_id = ?
        ORDER BY id DESC
        LIMIT ?
      `).all(taskId || 0, limit || 200);
    } else {
      rows = db.prepare(`
        SELECT * FROM invites
        ORDER BY id DESC
        LIMIT ?
      `).all(limit || 200);
    }
    return rows.map((row) => ({
      id: row.id,
      taskId: row.task_id || 0,
      userId: row.user_id,
      username: row.username || "",
      accountId: row.account_id || 0,
      accountPhone: row.account_phone || "",
      sourceChat: row.source_chat || "",
      action: row.action || "invite",
      skippedReason: row.skipped_reason || "",
      invitedAt: row.invited_at,
      status: row.status,
      error: row.error
    }));
  }

  function clearInvites(taskId) {
    if (taskId == null) {
      db.prepare("DELETE FROM invites").run();
      db.prepare("DELETE FROM invite_queue").run();
      return;
    }
    db.prepare("DELETE FROM invites WHERE task_id = ?").run(taskId || 0);
    db.prepare("DELETE FROM invite_queue WHERE task_id = ?").run(taskId || 0);
  }

  return {
    getSettings,
    saveSettings,
    listAccounts,
    findAccountByIdentity,
    clearAllData,
    listTasks,
    getTask,
    saveTask,
    deleteTask,
    listTaskCompetitors,
    setTaskCompetitors,
    listTaskAccounts,
    listAllTaskAccounts,
    setTaskAccounts,
    listLogs,
    listInvites,
    clearLogs,
    clearInvites,
    setAccountCooldown,
    clearAccountCooldown,
    addAccountEvent,
    listAccountEvents,
    deleteAccount,
    updateAccountIdentity,
    addAccount,
    updateAccountStatus,
    enqueueInvite,
    getPendingInvites,
    getPendingCount,
    clearQueue,
    markInviteStatus,
    incrementInviteAttempt,
    recordInvite,
    countInvitesToday,
    countInvitesTodayByAccount,
    addLog
  };
}

module.exports = { initStore };
