import type {
  Channel,
  NormalizedMessage,
  ResolvedCredentials,
  ProviderErrorCode,
} from '@zyphon/shared-types';
import type {
  ProviderAdapter,
  ProviderResult,
  ProviderHealth,
  WebhookRequest,
  DeliveryEvent,
} from '@zyphon/provider-contracts';

export class ZohoEmailProviderAdapter implements ProviderAdapter {
  public readonly id: string;
  public readonly channel: Channel = 'email';
  private fetchFn: typeof fetch;

  constructor(options: { id?: string; fetchFn?: typeof fetch } = {}) {
    this.id = options.id || 'zoho';
    this.fetchFn = options.fetchFn ?? globalThis.fetch.bind(globalThis);
  }

  public async validateConfiguration(c: ResolvedCredentials): Promise<void> {
    const token = c.token || c.auth_token || c.authToken || c.access_token;
    if (!token) {
      throw new Error("Zoho ZeptoMail / Email configuration requires 'token' or 'auth_token'");
    }
  }

  public async send(
    m: NormalizedMessage,
    c: ResolvedCredentials,
    signal: AbortSignal
  ): Promise<ProviderResult> {
    try {
      await this.validateConfiguration(c);
    } catch (err: any) {
      return { success: false, error: { code: 'CREDENTIAL_ERROR', message: err.message } };
    }

    const fromAddress = String(c.from || 'noreply@zyphon.local');
    const isZeptoMail = Boolean(c.token || (c.region && String(c.region).includes('zeptomail')));

    if (isZeptoMail) {
      const rawToken = String(c.token || c.auth_token || c.authToken || c.access_token || '');
      const authHeader = rawToken.startsWith('Zoho-enczapikey')
        ? rawToken
        : `Zoho-enczapikey ${rawToken}`;
      const regionHost = String(c.region || 'zeptomail.zoho.in').replace(/^https?:\/\//, '').replace(/^api\./, '');
      const url = `https://api.${regionHost}/v1.1/email`;

      const zeptoPayload: Record<string, unknown> = {
        from: { address: fromAddress, ...(c.from_name ? { name: String(c.from_name) } : {}) },
        to: [{ email_address: { address: m.recipient } }],
        subject: m.content.subject || '(No Subject)',
        ...(m.content.type === 'html'
          ? { htmlbody: m.content.body }
          : { textbody: m.content.body, htmlbody: `<pre>${m.content.body}</pre>` }),
      };

      if (m.content.attachments && m.content.attachments.length > 0) {
        zeptoPayload.attachments = m.content.attachments.map((a) => ({
          name: a.filename,
          mime_type: a.contentType || 'application/octet-stream',
          content: a.content,
        }));
      }

      try {
        const res = await this.fetchFn(url, {
          method: 'POST',
          headers: {
            Accept: 'application/json',
            'Content-Type': 'application/json',
            Authorization: authHeader,
          },
          body: JSON.stringify(zeptoPayload),
          signal,
        });

        const resData: any = await res.json().catch(() => ({}));

        if (res.ok && (res.status === 200 || res.status === 201)) {
          const ref = resData.request_id || resData.data?.[0]?.message_id || `zepto_${Date.now()}`;
          return {
            success: true,
            provider_reference: String(ref),
            raw_response: resData,
          };
        }

        let code: ProviderErrorCode = 'PROVIDER_5XX';
        if (res.status === 401 || res.status === 403) code = 'CREDENTIAL_ERROR';
        else if (res.status >= 400 && res.status < 500 && res.status !== 429) code = 'PROVIDER_REJECTED';
        else if (res.status === 429) code = 'PROVIDER_UNAVAILABLE';

        return {
          success: false,
          error: {
            code,
            message:
              resData.error?.details?.[0]?.message ||
              resData.error?.message ||
              resData.message ||
              `Zoho ZeptoMail error status: ${res.status}`,
            raw: resData,
          },
        };
      } catch (err: any) {
        const isTimeout = signal.aborted || err.name === 'AbortError';
        return {
          success: false,
          error: {
            code: isTimeout ? 'TIMEOUT' : 'PROVIDER_5XX',
            message: err.message || 'Zoho ZeptoMail dispatch failed',
          },
        };
      }
    }

    const token = String(c.auth_token || c.authToken || c.access_token || '');
    const accountId = String(c.account_id || c.accountId || 'current');

    const url = `https://mail.zoho.com/api/accounts/${encodeURIComponent(accountId)}/messages`;

    const bodyObj: Record<string, unknown> = {
      fromAddress,
      toAddress: m.recipient,
      subject: m.content.subject || '(No Subject)',
      content: m.content.body,
      mailFormat: m.content.type === 'html' ? 'html' : 'plaintext',
    };

    try {
      const res = await this.fetchFn(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Authorization: `Zoho-oauthtoken ${token}`,
        },
        body: JSON.stringify(bodyObj),
        signal,
      });

      const resData: any = await res.json().catch(() => ({}));

      if (res.status === 200 && resData.status?.code === 200) {
        return {
          success: true,
          provider_reference: resData.data?.messageId || `zoho_${Date.now()}`,
          raw_response: resData,
        };
      }

      let code: ProviderErrorCode = 'PROVIDER_5XX';
      if (res.status === 401 || res.status === 403) code = 'CREDENTIAL_ERROR';
      else if (res.status === 400) code = 'PROVIDER_REJECTED';
      else if (res.status === 429) code = 'PROVIDER_UNAVAILABLE';

      return {
        success: false,
        error: {
          code,
          message: resData.status?.description || `Zoho error status: ${res.status}`,
          raw: resData,
        },
      };
    } catch (err: any) {
      const isTimeout = signal.aborted || err.name === 'AbortError';
      return {
        success: false,
        error: {
          code: isTimeout ? 'TIMEOUT' : 'PROVIDER_5XX',
          message: err.message || 'Zoho dispatch failed',
        },
      };
    }
  }

  public async healthCheck(c: ResolvedCredentials): Promise<ProviderHealth> {
    const startTime = Date.now();
    const isZeptoMail = Boolean(c.token || (c.region && String(c.region).includes('zeptomail')));
    if (isZeptoMail) {
      try {
        await this.validateConfiguration(c);
        return { status: 'healthy', latency_ms: Date.now() - startTime, checked_at: Date.now() };
      } catch (err: any) {
        return { status: 'down', latency_ms: Date.now() - startTime, checked_at: Date.now(), message: err.message };
      }
    }

    const token = String(c.auth_token || c.authToken || c.access_token || '');
    if (!token) {
      return { status: 'down', latency_ms: 0, checked_at: Date.now(), message: 'Missing token' };
    }

    try {
      const res = await this.fetchFn('https://mail.zoho.com/api/accounts', {
        method: 'GET',
        headers: { Authorization: `Zoho-oauthtoken ${token}` },
      });

      const latency = Date.now() - startTime;
      if (res.ok) {
        return { status: 'healthy', latency_ms: latency, checked_at: Date.now() };
      }
      return { status: 'down', latency_ms: latency, checked_at: Date.now(), message: `HTTP ${res.status}` };
    } catch (err: any) {
      return { status: 'down', latency_ms: Date.now() - startTime, checked_at: Date.now(), message: err.message };
    }
  }

  public parseWebhook(_req: WebhookRequest): DeliveryEvent | null {
    return null;
  }
}

