import { sql } from '../../db.js';
import { ticketService } from '../ticketService.js';
import { messagingService } from '../messagingService.js';
import { auditService } from '../auditService.js';
import { integrationSettingsService } from './integrationSettingsService.js';
import { syncToOdoo } from './providers/odoo.js';
import { syncToTrello } from './providers/trello.js';
import { syncToAtlassian } from './providers/atlassian.js';
import { syncToGitHub } from './providers/github.js';
import type { IntegrationProvider, SyncPreview, SyncResult } from './types.js';

function truncate(s: string, max: number): string {
  const t = s.trim();
  if (t.length <= max) return t;
  return `${t.slice(0, max - 1)}…`;
}

/** Compact description — contact + priority + a few message snippets (not the full thread). */
async function buildDefaultDescription(ticketId: string): Promise<{
  title: string;
  description: string;
  priority: string;
  contact: string;
}> {
  const ticket = await ticketService.getById(ticketId);
  if (!ticket) throw Object.assign(new Error('Ticket not found'), { statusCode: 404 });

  const contactName =
    ticket.contact?.display_name?.trim()
    || ticket.portal_participant?.company_name
    || ticket.contact?.phone_number
    || 'Unknown contact';
  const phone = ticket.contact?.phone_number ?? '';
  const title = truncate(
    ticket.wa_group_subject?.trim()
      || `${contactName}${ticket.priority === 'high' ? ' (high)' : ''}`,
    120,
  );

  const messages = await messagingService.getMessages(ticketId);
  const snippets: string[] = [];
  const inbound = messages.filter(m => m.direction === 'inbound');
  const firstIn = inbound[0];
  const lastIn = inbound.length > 1 ? inbound[inbound.length - 1] : null;
  if (firstIn) snippets.push(`Customer: ${truncate(firstIn.content, 220)}`);
  if (lastIn && lastIn.id !== firstIn?.id) {
    snippets.push(`Latest: ${truncate(lastIn.content, 180)}`);
  }
  const lastOut = [...messages].reverse().find(m => m.direction === 'outbound');
  if (lastOut) snippets.push(`Staff: ${truncate(lastOut.content, 160)}`);

  const description = [
    `Silicon Support ticket ${ticket.id.slice(0, 8)}`,
    `Contact: ${contactName}${phone ? ` (${phone})` : ''}`,
    `Priority: ${ticket.priority} · Status: ${ticket.status}`,
    ticket.channel?.name ? `Channel: ${ticket.channel.name}` : null,
    '',
    ...snippets,
  ].filter(line => line != null).join('\n');

  return {
    title,
    description: truncate(description, 2500),
    priority: ticket.priority,
    contact: contactName,
  };
}

async function getLink(ticketId: string, provider: IntegrationProvider) {
  const [row] = await sql<Array<{ external_id: string; external_url: string | null }>>`
    SELECT external_id, external_url FROM ticket_integration_links
    WHERE ticket_id = ${ticketId} AND provider = ${provider}
  `;
  return row ?? null;
}

export const ticketSyncService = {
  async preview(ticketId: string, provider: IntegrationProvider): Promise<SyncPreview> {
    const settings = await integrationSettingsService.getSecrets(provider);
    if (!settings?.enabled) {
      throw Object.assign(new Error(`${provider} integration is not enabled`), { statusCode: 400 });
    }
    const defaults = await buildDefaultDescription(ticketId);
    const link = await getLink(ticketId, provider);

    const fields = [
      { key: 'title', label: 'Title', value: defaults.title, required: true },
      { key: 'description', label: 'Description', value: defaults.description, required: true },
      { key: 'priority', label: 'Priority', value: defaults.priority },
      { key: 'contact', label: 'Contact (reference)', value: defaults.contact },
    ];

    return {
      provider,
      already_synced: Boolean(link),
      external_url: link?.external_url ?? null,
      fields,
    };
  },

  async sync(
    ticketId: string,
    provider: IntegrationProvider,
    actorId: string | null,
    overrides: Record<string, string>,
  ): Promise<SyncResult> {
    const settings = await integrationSettingsService.getSecrets(provider);
    if (!settings?.enabled) {
      throw Object.assign(new Error(`${provider} integration is not enabled`), { statusCode: 400 });
    }

    const existing = await getLink(ticketId, provider);
    if (existing) {
      throw Object.assign(new Error(`Already synced to ${provider}`), {
        statusCode: 409,
        external_url: existing.external_url,
      });
    }

    const preview = await this.preview(ticketId, provider);
    const merged: Record<string, string> = {};
    for (const f of preview.fields) {
      merged[f.key] = overrides[f.key]?.trim() || f.value;
    }
    if (!merged.title || !merged.description) {
      throw Object.assign(new Error('title and description are required'), { statusCode: 400 });
    }

    const payload = {
      title: merged.title,
      description: merged.description,
      priority: merged.priority,
    };

    let result: SyncResult;
    switch (provider) {
      case 'odoo': {
        const url = settings.config.url?.trim();
        const database = settings.config.database?.trim();
        const username = settings.config.username?.trim();
        const model = settings.config.model?.trim() || 'helpdesk.ticket';
        const key = settings.secrets.apiKey || settings.secrets.password;
        if (!url || !database || !username || !key) {
          throw Object.assign(new Error('Odoo needs URL, database, username and API key'), { statusCode: 400 });
        }
        const r = await syncToOdoo(
          { url, database, username, apiKeyOrPassword: key, model },
          payload,
        );
        result = { provider, ...r };
        break;
      }
      case 'trello': {
        const listId = settings.config.list_id?.trim();
        const apiKey = settings.secrets.apiKey;
        const token = settings.secrets.token;
        if (!listId || !apiKey || !token) {
          throw Object.assign(new Error('Trello needs API key, token and list_id'), { statusCode: 400 });
        }
        const r = await syncToTrello({ apiKey, token, listId }, payload);
        result = { provider, ...r };
        break;
      }
      case 'atlassian': {
        const siteUrl = settings.config.site_url?.trim();
        const email = settings.config.email?.trim();
        const projectKey = settings.config.project_key?.trim();
        const issueType = settings.config.issue_type?.trim() || 'Task';
        const apiToken = settings.secrets.apiKey || settings.secrets.token;
        if (!siteUrl || !email || !projectKey || !apiToken) {
          throw Object.assign(new Error('Atlassian needs site URL, email, API token and project key'), { statusCode: 400 });
        }
        const r = await syncToAtlassian(
          { siteUrl, email, apiToken, projectKey, issueType },
          payload,
        );
        result = { provider, ...r };
        break;
      }
      case 'github': {
        const owner = settings.config.owner?.trim();
        const repo = settings.config.repo?.trim();
        const token = settings.secrets.token || settings.secrets.apiKey;
        if (!owner || !repo || !token) {
          throw Object.assign(new Error('GitHub needs owner, repo and token'), { statusCode: 400 });
        }
        const r = await syncToGitHub({ token, owner, repo }, payload);
        result = { provider, ...r };
        break;
      }
      default: {
        const _exhaustive: never = provider;
        throw new Error(`Unsupported provider: ${_exhaustive}`);
      }
    }

    await sql`
      INSERT INTO ticket_integration_links (ticket_id, provider, external_id, external_url)
      VALUES (${ticketId}, ${provider}, ${result.external_id}, ${result.external_url})
    `;
    await auditService.log(
      actorId,
      actorId ? 'user' : 'system',
      'ticket.integration_synced',
      'ticket',
      ticketId,
      {
      provider,
      external_id: result.external_id,
      external_url: result.external_url,
    });
    return result;
  },

  async listLinks(ticketId: string) {
    return sql<Array<{ provider: IntegrationProvider; external_id: string; external_url: string | null; created_at: string }>>`
      SELECT provider, external_id, external_url, created_at
      FROM ticket_integration_links WHERE ticket_id = ${ticketId}
      ORDER BY created_at DESC
    `;
  },
};
