import Database from 'better-sqlite3';
import path from 'path';

const DB_PATH = process.env.DATABASE_PATH || path.join(process.cwd(), 'data', 'closebot-sms.db');

let _db: Database.Database | null = null;

export function getDb(): Database.Database {
  if (!_db) {
    const fs = require('fs');
    const dir = path.dirname(DB_PATH);
    if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });

    _db = new Database(DB_PATH);
    _db.pragma('journal_mode = WAL');
    _db.pragma('foreign_keys = ON');
    initSchema(_db);
  }
  return _db;
}

function initSchema(db: Database.Database) {
  db.exec(`
    CREATE TABLE IF NOT EXISTS routes (
      id TEXT PRIMARY KEY,
      twilio_number TEXT NOT NULL UNIQUE,
      twilio_number_sid TEXT,
      closebot_source_id TEXT NOT NULL,
      closebot_bot_id TEXT,
      bot_name TEXT,
      greeting_message TEXT,
      after_hours_reply TEXT DEFAULT 'Thanks for reaching out! We are currently offline and will get back to you during business hours.',
      business_hours_json TEXT DEFAULT '{"mon":{"start":"09:00","end":"17:00"},"tue":{"start":"09:00","end":"17:00"},"wed":{"start":"09:00","end":"17:00"},"thu":{"start":"09:00","end":"17:00"},"fri":{"start":"09:00","end":"17:00"}}',
      max_concurrent INTEGER DEFAULT 50,
      active INTEGER DEFAULT 1,
      created_at TEXT DEFAULT (datetime('now')),
      updated_at TEXT DEFAULT (datetime('now'))
    );

    CREATE TABLE IF NOT EXISTS contacts (
      id TEXT PRIMARY KEY,
      phone TEXT NOT NULL UNIQUE,
      name TEXT,
      email TEXT,
      status TEXT DEFAULT 'new',
      closebot_lead_id TEXT,
      assigned_bot_id TEXT,
      assigned_route_id TEXT REFERENCES routes(id),
      tags TEXT DEFAULT '[]',
      fields_json TEXT DEFAULT '{}',
      first_contact TEXT DEFAULT (datetime('now')),
      last_contact TEXT DEFAULT (datetime('now')),
      message_count INTEGER DEFAULT 0
    );

    CREATE TABLE IF NOT EXISTS messages (
      id TEXT PRIMARY KEY,
      contact_id TEXT REFERENCES contacts(id),
      route_id TEXT REFERENCES routes(id),
      direction TEXT NOT NULL,
      source TEXT NOT NULL,
      body TEXT NOT NULL,
      twilio_sid TEXT,
      twilio_status TEXT,
      closebot_message_id TEXT,
      created_at TEXT DEFAULT (datetime('now'))
    );

    CREATE TABLE IF NOT EXISTS settings (
      key TEXT PRIMARY KEY,
      value TEXT NOT NULL,
      updated_at TEXT DEFAULT (datetime('now'))
    );

    CREATE TABLE IF NOT EXISTS events (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      type TEXT NOT NULL,
      contact_id TEXT REFERENCES contacts(id),
      route_id TEXT REFERENCES routes(id),
      data_json TEXT,
      created_at TEXT DEFAULT (datetime('now'))
    );

    CREATE INDEX IF NOT EXISTS idx_messages_contact ON messages(contact_id);
    CREATE INDEX IF NOT EXISTS idx_messages_created ON messages(created_at);
    CREATE INDEX IF NOT EXISTS idx_contacts_status ON contacts(status);
    CREATE INDEX IF NOT EXISTS idx_contacts_phone ON contacts(phone);
    CREATE INDEX IF NOT EXISTS idx_events_created ON events(created_at);

    -- A2P 10DLC Registration tables
    CREATE TABLE IF NOT EXISTS a2p_registrations (
      id TEXT PRIMARY KEY,
      business_name TEXT NOT NULL,
      status TEXT NOT NULL DEFAULT 'pending',
      input_json TEXT NOT NULL,
      sid_chain_json TEXT DEFAULT '{}',
      landing_page_url TEXT,
      privacy_policy_url TEXT,
      terms_url TEXT,
      brand_trust_score INTEGER,
      failure_reason TEXT,
      attempt_count INTEGER DEFAULT 0,
      max_attempts INTEGER DEFAULT 3,
      created_at TEXT DEFAULT (datetime('now')),
      updated_at TEXT DEFAULT (datetime('now')),
      brand_submitted_at TEXT,
      brand_resolved_at TEXT,
      campaign_submitted_at TEXT,
      campaign_resolved_at TEXT,
      completed_at TEXT
    );

    CREATE INDEX IF NOT EXISTS idx_a2p_status ON a2p_registrations(status);
    CREATE INDEX IF NOT EXISTS idx_a2p_created ON a2p_registrations(created_at);

    -- Landing Pages table
    CREATE TABLE IF NOT EXISTS landing_pages (
      id TEXT PRIMARY KEY,
      business_name TEXT NOT NULL,
      business_slug TEXT NOT NULL,
      config_json TEXT NOT NULL,
      opt_in_html TEXT,
      privacy_html TEXT,
      terms_html TEXT,
      status TEXT DEFAULT 'draft',
      published_url TEXT,
      a2p_registration_id TEXT,
      created_at TEXT DEFAULT (datetime('now')),
      updated_at TEXT DEFAULT (datetime('now'))
    );

    CREATE INDEX IF NOT EXISTS idx_landing_pages_slug ON landing_pages(business_slug);
    CREATE INDEX IF NOT EXISTS idx_landing_pages_status ON landing_pages(status);
  `);
}

// --- Query helpers ---

export function getStats() {
  const db = getDb();
  const today = new Date().toISOString().split('T')[0];
  return {
    activeConversations: db.prepare(`SELECT COUNT(DISTINCT contact_id) as c FROM messages WHERE created_at >= datetime('now', '-24 hours')`).get() as { c: number },
    messagesToday: db.prepare(`SELECT COUNT(*) as c FROM messages WHERE created_at >= ?`).get(today) as { c: number },
    totalContacts: db.prepare(`SELECT COUNT(*) as c FROM contacts`).get() as { c: number },
    totalMessages: db.prepare(`SELECT COUNT(*) as c FROM messages`).get() as { c: number },
  };
}

export function getRecentEvents(limit = 20) {
  const db = getDb();
  return db.prepare(`
    SELECT e.*, c.phone, c.name, r.bot_name
    FROM events e
    LEFT JOIN contacts c ON e.contact_id = c.id
    LEFT JOIN routes r ON e.route_id = r.id
    ORDER BY e.created_at DESC
    LIMIT ?
  `).all(limit);
}

export function getConversations(opts: { search?: string; status?: string; limit?: number; offset?: number }) {
  const db = getDb();
  let where = '1=1';
  const params: unknown[] = [];

  if (opts.search) {
    where += ` AND (c.name LIKE ? OR c.phone LIKE ?)`;
    params.push(`%${opts.search}%`, `%${opts.search}%`);
  }
  if (opts.status) {
    where += ` AND c.status = ?`;
    params.push(opts.status);
  }

  params.push(opts.limit || 50, opts.offset || 0);

  return db.prepare(`
    SELECT c.*, r.bot_name,
      (SELECT body FROM messages WHERE contact_id = c.id ORDER BY created_at DESC LIMIT 1) as last_message,
      (SELECT created_at FROM messages WHERE contact_id = c.id ORDER BY created_at DESC LIMIT 1) as last_message_at,
      (SELECT COUNT(*) FROM messages WHERE contact_id = c.id AND direction = 'inbound' AND twilio_status IS NULL) as unread_count
    FROM contacts c
    LEFT JOIN routes r ON c.assigned_route_id = r.id
    WHERE ${where}
    ORDER BY c.last_contact DESC
    LIMIT ? OFFSET ?
  `).all(...params);
}

export function getMessages(contactId: string, limit = 100) {
  const db = getDb();
  return db.prepare(`
    SELECT * FROM messages WHERE contact_id = ? ORDER BY created_at ASC LIMIT ?
  `).all(contactId, limit);
}

export function getContact(id: string) {
  const db = getDb();
  return db.prepare(`SELECT * FROM contacts WHERE id = ?`).get(id);
}

export function getContactByPhone(phone: string) {
  const db = getDb();
  return db.prepare(`SELECT * FROM contacts WHERE phone = ?`).get(phone);
}

export function getRoutes() {
  const db = getDb();
  return db.prepare(`SELECT * FROM routes ORDER BY created_at DESC`).all();
}

export function getRouteByNumber(twilioNumber: string) {
  const db = getDb();
  return db.prepare(`SELECT * FROM routes WHERE twilio_number = ? AND active = 1`).get(twilioNumber);
}

export function getRoute(id: string) {
  const db = getDb();
  return db.prepare(`SELECT * FROM routes WHERE id = ?`).get(id);
}

export function getAllContacts(opts: { search?: string; status?: string; bot?: string; limit?: number; offset?: number }) {
  const db = getDb();
  let where = '1=1';
  const params: unknown[] = [];

  if (opts.search) {
    where += ` AND (c.name LIKE ? OR c.phone LIKE ? OR c.email LIKE ?)`;
    params.push(`%${opts.search}%`, `%${opts.search}%`, `%${opts.search}%`);
  }
  if (opts.status) {
    where += ` AND c.status = ?`;
    params.push(opts.status);
  }
  if (opts.bot) {
    where += ` AND c.assigned_bot_id = ?`;
    params.push(opts.bot);
  }

  params.push(opts.limit || 50, opts.offset || 0);

  return db.prepare(`
    SELECT c.*, r.bot_name
    FROM contacts c
    LEFT JOIN routes r ON c.assigned_route_id = r.id
    WHERE ${where}
    ORDER BY c.last_contact DESC
    LIMIT ? OFFSET ?
  `).all(...params);
}

export function getMessageStats(startDate?: string, endDate?: string) {
  const db = getDb();
  const start = startDate || new Date(Date.now() - 30 * 86400000).toISOString();
  const end = endDate || new Date().toISOString();

  return {
    daily: db.prepare(`
      SELECT date(created_at) as date, direction, COUNT(*) as count
      FROM messages
      WHERE created_at BETWEEN ? AND ?
      GROUP BY date(created_at), direction
      ORDER BY date
    `).all(start, end),
    byBot: db.prepare(`
      SELECT r.bot_name, COUNT(*) as count
      FROM messages m
      JOIN routes r ON m.route_id = r.id
      WHERE m.created_at BETWEEN ? AND ?
      GROUP BY r.bot_name
      ORDER BY count DESC
    `).all(start, end),
    byStatus: db.prepare(`
      SELECT status, COUNT(*) as count
      FROM contacts
      GROUP BY status
    `).all(),
  };
}

// --- Insert helpers ---

export function insertContact(data: { id: string; phone: string; name?: string; assigned_route_id?: string; assigned_bot_id?: string }) {
  const db = getDb();
  return db.prepare(`
    INSERT OR IGNORE INTO contacts (id, phone, name, assigned_route_id, assigned_bot_id)
    VALUES (?, ?, ?, ?, ?)
  `).run(data.id, data.phone, data.name || null, data.assigned_route_id || null, data.assigned_bot_id || null);
}

export function insertMessage(data: { id: string; contact_id: string; route_id?: string; direction: string; source: string; body: string; twilio_sid?: string }) {
  const db = getDb();
  db.prepare(`
    INSERT INTO messages (id, contact_id, route_id, direction, source, body, twilio_sid)
    VALUES (?, ?, ?, ?, ?, ?, ?)
  `).run(data.id, data.contact_id, data.route_id || null, data.direction, data.source, data.body, data.twilio_sid || null);

  // Update contact last_contact and message_count
  db.prepare(`
    UPDATE contacts SET last_contact = datetime('now'), message_count = message_count + 1 WHERE id = ?
  `).run(data.contact_id);
}

export function insertEvent(data: { type: string; contact_id?: string; route_id?: string; data_json?: string }) {
  const db = getDb();
  return db.prepare(`
    INSERT INTO events (type, contact_id, route_id, data_json)
    VALUES (?, ?, ?, ?)
  `).run(data.type, data.contact_id || null, data.route_id || null, data.data_json || null);
}

export function upsertSetting(key: string, value: string) {
  const db = getDb();
  return db.prepare(`
    INSERT INTO settings (key, value) VALUES (?, ?)
    ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = datetime('now')
  `).run(key, value);
}

export function getSetting(key: string): string | undefined {
  const db = getDb();
  const row = db.prepare(`SELECT value FROM settings WHERE key = ?`).get(key) as { value: string } | undefined;
  return row?.value;
}

// ============================================================
// A2P Registration repository functions
// ============================================================

export interface A2PRegistrationRow {
  id: string;
  business_name: string;
  status: string;
  input_json: string;
  sid_chain_json: string;
  landing_page_url: string | null;
  privacy_policy_url: string | null;
  terms_url: string | null;
  brand_trust_score: number | null;
  failure_reason: string | null;
  attempt_count: number;
  max_attempts: number;
  created_at: string;
  updated_at: string;
  brand_submitted_at: string | null;
  brand_resolved_at: string | null;
  campaign_submitted_at: string | null;
  campaign_resolved_at: string | null;
  completed_at: string | null;
}

export function createA2PRegistration(data: {
  id: string;
  business_name: string;
  status: string;
  input_json: string;
}) {
  const db = getDb();
  return db.prepare(`
    INSERT INTO a2p_registrations (id, business_name, status, input_json)
    VALUES (?, ?, ?, ?)
  `).run(data.id, data.business_name, data.status, data.input_json);
}

export function getA2PRegistration(id: string): A2PRegistrationRow | undefined {
  const db = getDb();
  return db.prepare(`SELECT * FROM a2p_registrations WHERE id = ?`).get(id) as A2PRegistrationRow | undefined;
}

export function listA2PRegistrations(opts?: { status?: string; limit?: number; offset?: number }): A2PRegistrationRow[] {
  const db = getDb();
  let where = '1=1';
  const params: unknown[] = [];

  if (opts?.status) {
    where += ` AND status = ?`;
    params.push(opts.status);
  }

  params.push(opts?.limit || 50, opts?.offset || 0);

  return db.prepare(`
    SELECT * FROM a2p_registrations
    WHERE ${where}
    ORDER BY created_at DESC
    LIMIT ? OFFSET ?
  `).all(...params) as A2PRegistrationRow[];
}

export function updateA2PStatus(id: string, status: string, failureReason?: string) {
  const db = getDb();
  const now = new Date().toISOString().replace('T', ' ').slice(0, 19);

  if (failureReason !== undefined) {
    return db.prepare(`
      UPDATE a2p_registrations
      SET status = ?, failure_reason = ?, updated_at = ?,
          attempt_count = attempt_count + 1
      WHERE id = ?
    `).run(status, failureReason, now, id);
  }

  // Handle timestamp fields based on status
  let extraSet = '';
  if (status === 'brand_pending') extraSet = `, brand_submitted_at = '${now}'`;
  if (status === 'brand_approved' || status === 'brand_failed') extraSet = `, brand_resolved_at = '${now}'`;
  if (status === 'campaign_pending') extraSet = `, campaign_submitted_at = '${now}'`;
  if (status === 'campaign_approved' || status === 'campaign_failed') extraSet = `, campaign_resolved_at = '${now}'`;
  if (status === 'completed') extraSet = `, completed_at = '${now}'`;

  return db.prepare(`
    UPDATE a2p_registrations
    SET status = ?, updated_at = ?${extraSet}
    WHERE id = ?
  `).run(status, now, id);
}

export function updateA2PSidChain(id: string, sidChainJson: string) {
  const db = getDb();
  const now = new Date().toISOString().replace('T', ' ').slice(0, 19);
  return db.prepare(`
    UPDATE a2p_registrations
    SET sid_chain_json = ?, updated_at = ?
    WHERE id = ?
  `).run(sidChainJson, now, id);
}

export function updateA2PRegistration(id: string, updates: Partial<Pick<A2PRegistrationRow, 'input_json' | 'business_name' | 'landing_page_url' | 'privacy_policy_url' | 'terms_url' | 'brand_trust_score'>>) {
  const db = getDb();
  const now = new Date().toISOString().replace('T', ' ').slice(0, 19);
  const sets: string[] = ['updated_at = ?'];
  const params: unknown[] = [now];

  if (updates.input_json !== undefined) { sets.push('input_json = ?'); params.push(updates.input_json); }
  if (updates.business_name !== undefined) { sets.push('business_name = ?'); params.push(updates.business_name); }
  if (updates.landing_page_url !== undefined) { sets.push('landing_page_url = ?'); params.push(updates.landing_page_url); }
  if (updates.privacy_policy_url !== undefined) { sets.push('privacy_policy_url = ?'); params.push(updates.privacy_policy_url); }
  if (updates.terms_url !== undefined) { sets.push('terms_url = ?'); params.push(updates.terms_url); }
  if (updates.brand_trust_score !== undefined) { sets.push('brand_trust_score = ?'); params.push(updates.brand_trust_score); }

  params.push(id);

  return db.prepare(`UPDATE a2p_registrations SET ${sets.join(', ')} WHERE id = ?`).run(...params);
}

export function deleteA2PRegistration(id: string) {
  const db = getDb();
  return db.prepare(`DELETE FROM a2p_registrations WHERE id = ?`).run(id);
}

export function getA2PStats() {
  const db = getDb();
  const total = db.prepare(`SELECT COUNT(*) as c FROM a2p_registrations`).get() as { c: number };
  const pending = db.prepare(`SELECT COUNT(*) as c FROM a2p_registrations WHERE status IN ('pending', 'creating_profile', 'profile_submitted', 'creating_brand', 'brand_pending', 'creating_campaign', 'campaign_pending')`).get() as { c: number };
  const approved = db.prepare(`SELECT COUNT(*) as c FROM a2p_registrations WHERE status IN ('completed', 'campaign_approved')`).get() as { c: number };
  const failed = db.prepare(`SELECT COUNT(*) as c FROM a2p_registrations WHERE status IN ('brand_failed', 'campaign_failed', 'manual_review')`).get() as { c: number };

  return {
    total: total.c,
    pending: pending.c,
    approved: approved.c,
    failed: failed.c,
  };
}

// ============================================================
// Landing Pages repository functions
// ============================================================

export interface LandingPageRow {
  id: string;
  business_name: string;
  business_slug: string;
  config_json: string;
  opt_in_html: string | null;
  privacy_html: string | null;
  terms_html: string | null;
  status: string;
  published_url: string | null;
  a2p_registration_id: string | null;
  created_at: string;
  updated_at: string;
}

export function createLandingPage(data: {
  id: string;
  business_name: string;
  business_slug: string;
  config_json: string;
  opt_in_html: string;
  privacy_html: string;
  terms_html: string;
  status?: string;
  a2p_registration_id?: string;
}) {
  const db = getDb();
  return db.prepare(`
    INSERT INTO landing_pages (id, business_name, business_slug, config_json, opt_in_html, privacy_html, terms_html, status, a2p_registration_id)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
  `).run(
    data.id,
    data.business_name,
    data.business_slug,
    data.config_json,
    data.opt_in_html,
    data.privacy_html,
    data.terms_html,
    data.status || 'draft',
    data.a2p_registration_id || null
  );
}

export function getLandingPage(id: string): LandingPageRow | undefined {
  const db = getDb();
  return db.prepare(`SELECT * FROM landing_pages WHERE id = ?`).get(id) as LandingPageRow | undefined;
}

export function listLandingPages(opts?: { status?: string; limit?: number; offset?: number }): LandingPageRow[] {
  const db = getDb();
  let where = '1=1';
  const params: unknown[] = [];

  if (opts?.status) {
    where += ` AND status = ?`;
    params.push(opts.status);
  }

  params.push(opts?.limit || 50, opts?.offset || 0);

  return db.prepare(`
    SELECT * FROM landing_pages
    WHERE ${where}
    ORDER BY created_at DESC
    LIMIT ? OFFSET ?
  `).all(...params) as LandingPageRow[];
}

export function updateLandingPage(id: string, updates: Partial<Omit<LandingPageRow, 'id' | 'created_at'>>) {
  const db = getDb();
  const now = new Date().toISOString().replace('T', ' ').slice(0, 19);
  const sets: string[] = ['updated_at = ?'];
  const params: unknown[] = [now];

  if (updates.business_name !== undefined) { sets.push('business_name = ?'); params.push(updates.business_name); }
  if (updates.business_slug !== undefined) { sets.push('business_slug = ?'); params.push(updates.business_slug); }
  if (updates.config_json !== undefined) { sets.push('config_json = ?'); params.push(updates.config_json); }
  if (updates.opt_in_html !== undefined) { sets.push('opt_in_html = ?'); params.push(updates.opt_in_html); }
  if (updates.privacy_html !== undefined) { sets.push('privacy_html = ?'); params.push(updates.privacy_html); }
  if (updates.terms_html !== undefined) { sets.push('terms_html = ?'); params.push(updates.terms_html); }
  if (updates.status !== undefined) { sets.push('status = ?'); params.push(updates.status); }
  if (updates.published_url !== undefined) { sets.push('published_url = ?'); params.push(updates.published_url); }
  if (updates.a2p_registration_id !== undefined) { sets.push('a2p_registration_id = ?'); params.push(updates.a2p_registration_id); }

  params.push(id);

  return db.prepare(`UPDATE landing_pages SET ${sets.join(', ')} WHERE id = ?`).run(...params);
}

export function deleteLandingPage(id: string) {
  const db = getDb();
  return db.prepare(`DELETE FROM landing_pages WHERE id = ?`).run(id);
}
