import { and, eq, sql } from 'drizzle-orm';
import { z } from 'zod';

import { db } from '@/lib/db';
import { clients, clientContacts } from '@/lib/db/schema/clients';
import { createClient } from '@/lib/services/clients.service';
import { normalizeEmail, normalizePhone } from '@/lib/dedup/normalize';

import type { ImportAdapter, MappedRow } from '../types';

/** Build the contacts array createClient requires from the flat email/phone
 *  columns. Phone carries its E.164 form (the contact schema requires it). */
function buildContacts(row: MappedRow) {
  const contacts: Array<{
    channel: 'email' | 'phone';
    value: string;
    valueE164?: string;
    valueCountry?: string;
    isPrimary: boolean;
  }> = [];
  if (row.email) contacts.push({ channel: 'email', value: row.email, isPrimary: true });
  if (row.phone) {
    const ph = normalizePhone(row.phone);
    contacts.push({
      channel: 'phone',
      value: row.phone,
      valueE164: ph?.e164 ?? undefined,
      valueCountry: ph?.country ?? undefined,
      isPrimary: !row.email,
    });
  }
  return contacts;
}

export const clientsAdapter: ImportAdapter = {
  key: 'clients',
  label: 'Clients',
  order: 2,
  dependsOn: [],
  targetFields: [
    {
      key: 'fullName',
      label: 'Full name',
      required: true,
      aliases: ['name', 'client', 'clientname'],
      zod: z.string().min(1),
    },
    { key: 'email', label: 'Email', required: false, zod: z.string().email() },
    { key: 'phone', label: 'Phone', required: false, aliases: ['mobile', 'tel'], zod: z.string() },
    {
      key: 'nationalityIso',
      label: 'Nationality (ISO-2)',
      required: false,
      aliases: ['nationality', 'country'],
      zod: z.string().length(2),
    },
    {
      key: 'preferredContactMethod',
      label: 'Preferred contact',
      required: false,
      zod: z.enum(['email', 'phone', 'whatsapp']),
    },
    {
      key: 'source',
      label: 'Source',
      required: false,
      zod: z.enum(['website', 'manual', 'referral', 'broker', 'other']),
    },
    { key: 'sourceDetails', label: 'Source details', required: false, zod: z.string() },
  ],
  extraValidate: (row) =>
    row.email || row.phone ? [] : [{ field: 'email', message: 'An email or phone is required' }],
  matchKey: (row) => {
    const email = normalizeEmail(row.email ?? null);
    if (email) return `email:${email}`;
    const ph = normalizePhone(row.phone ?? null);
    return ph?.e164 ? `phone:${ph.e164}` : null;
  },
  findExisting: async (portId, key) => {
    const idx = key.indexOf(':');
    const channel = key.slice(0, idx);
    const value = key.slice(idx + 1);
    const base = db
      .select({ id: clients.id })
      .from(clientContacts)
      .innerJoin(clients, eq(clients.id, clientContacts.clientId));
    const rows =
      channel === 'email'
        ? await base
            .where(
              and(
                eq(clients.portId, portId),
                eq(clientContacts.channel, 'email'),
                sql`lower(${clientContacts.value}) = ${value}`,
              ),
            )
            .limit(1)
        : await base
            .where(and(eq(clients.portId, portId), eq(clientContacts.valueE164, value)))
            .limit(1);
    return rows[0] ?? null;
  },
  insert: async (row, _resolved, ctx) => {
    const c = await createClient(
      ctx.portId,
      {
        fullName: row.fullName!,
        contacts: buildContacts(row),
        nationalityIso: row.nationalityIso,
        preferredContactMethod: row.preferredContactMethod as
          | 'email'
          | 'phone'
          | 'whatsapp'
          | undefined,
        source: row.source as 'website' | 'manual' | 'referral' | 'broker' | 'other' | undefined,
        sourceDetails: row.sourceDetails,
        tagIds: [],
      },
      ctx.meta,
    );
    return { id: c.id };
  },
  // Insert-only under the importer (v1): a client's contacts/addresses are a
  // graph the flat update can't safely overwrite. update-matches → skip.
};
