import net from 'node:net';
import { randomUUID } from 'node:crypto';
import { URL } from 'node:url';
import { getStorage } from '@bytelyst/storage';
import { BLOB_CONTAINERS, getBucket, isBlobStorageConfigured } from '../../lib/blob.js';
import { checkAllServices } from './health-checker.js';
import { resolveEmailConfig } from '../delivery/dispatcher.js';
import { sendEmail } from '../delivery/channels/email.js';
import type { PlatformStatus } from './types.js';

export type DiagnosticStatus = 'pass' | 'fail';

export interface DiagnosticCheck {
  name: string;
  status: DiagnosticStatus;
  durationMs: number;
  summary: string;
  details?: Record<string, unknown>;
  error?: string;
}

export interface DiagnosticsReport {
  status: 'ok' | 'degraded';
  checkedAt: string;
  dependencyChecks: DiagnosticCheck[];
  serviceStatus: PlatformStatus;
}

async function tcpConnect(host: string, port: number, timeoutMs: number): Promise<void> {
  await new Promise<void>((resolve, reject) => {
    const socket = net.createConnection({ host, port });
    const onError = (error: Error) => {
      socket.destroy();
      reject(error);
    };

    socket.setTimeout(timeoutMs, () => {
      socket.destroy();
      reject(new Error(`Timed out connecting to ${host}:${port}`));
    });
    socket.once('error', onError);
    socket.once('connect', () => {
      socket.end();
      resolve();
    });
  });
}

function checkStatus(ok: boolean): DiagnosticStatus {
  return ok ? 'pass' : 'fail';
}

function reportStatus(checks: DiagnosticCheck[], serviceStatus: PlatformStatus): 'ok' | 'degraded' {
  if (checks.some(check => check.status === 'fail')) return 'degraded';
  return serviceStatus.overall === 'operational' ? 'ok' : 'degraded';
}

export async function checkBlobStorageReadiness(): Promise<DiagnosticCheck> {
  const startedAt = Date.now();

  if (!isBlobStorageConfigured()) {
    return {
      name: 'blob_storage',
      status: 'fail',
      durationMs: Date.now() - startedAt,
      summary: 'Blob storage is not configured',
      details: {
        configured: false,
        provider: process.env.STORAGE_PROVIDER || 'azure',
      },
    };
  }

  try {
    const storage = await getStorage();
    const healthy = await storage.isHealthy();

    return {
      name: 'blob_storage',
      status: checkStatus(healthy),
      durationMs: Date.now() - startedAt,
      summary: healthy
        ? 'Blob storage provider is reachable'
        : 'Blob storage provider is not reachable',
      details: {
        configured: true,
        provider: process.env.STORAGE_PROVIDER || 'azure',
      },
    };
  } catch (error) {
    return {
      name: 'blob_storage',
      status: 'fail',
      durationMs: Date.now() - startedAt,
      summary: 'Blob storage readiness check failed',
      error: error instanceof Error ? error.message : String(error),
      details: {
        configured: true,
        provider: process.env.STORAGE_PROVIDER || 'azure',
      },
    };
  }
}

export async function runBlobStorageSelfTest(): Promise<DiagnosticCheck> {
  const startedAt = Date.now();
  const bucket = await getBucket(BLOB_CONTAINERS.attachments);
  const key = `self-test/platform-service/${new Date().toISOString().replace(/[:.]/g, '-')}-${randomUUID()}.txt`;
  const payload = `platform-service self-test ${new Date().toISOString()}`;

  try {
    const storage = await getStorage();
    const providerHealthy = await storage.isHealthy();

    await bucket.upload(key, payload, {
      contentType: 'text/plain',
      metadata: {
        source: 'platform-service-self-test',
      },
    });

    const existsAfterUpload = await bucket.exists(key);
    const listed = await bucket.list(key);
    const downloaded = (await bucket.download(key)).toString('utf8');
    const signedUrl = await bucket.getSignedUrl(key, { permissions: 'read', expiresIn: 300 });

    await bucket.delete(key);

    const existsAfterDelete = await bucket.exists(key);
    const signedUrlHost = new URL(signedUrl).host;
    const listedBlob = listed.find(blob => blob.key === key);
    const passed =
      providerHealthy &&
      existsAfterUpload &&
      !!listedBlob &&
      downloaded === payload &&
      !existsAfterDelete;

    return {
      name: 'blob_storage_self_test',
      status: checkStatus(passed),
      durationMs: Date.now() - startedAt,
      summary: passed
        ? 'Blob storage upload/list/download/sign/delete flow passed'
        : 'Blob storage self-test reported a failed assertion',
      details: {
        bucket: BLOB_CONTAINERS.attachments,
        key,
        provider: process.env.STORAGE_PROVIDER || 'azure',
        providerHealthy,
        existsAfterUpload,
        listed: !!listedBlob,
        downloadedMatches: downloaded === payload,
        existsAfterDelete,
        signedUrlHost,
      },
    };
  } catch (error) {
    try {
      await bucket.delete(key);
    } catch {
      // Best-effort cleanup for temporary self-test blobs.
    }

    return {
      name: 'blob_storage_self_test',
      status: 'fail',
      durationMs: Date.now() - startedAt,
      summary: 'Blob storage self-test failed',
      error: error instanceof Error ? error.message : String(error),
      details: {
        bucket: BLOB_CONTAINERS.attachments,
        key,
        provider: process.env.STORAGE_PROVIDER || 'azure',
      },
    };
  }
}

export async function checkSmtpRelayReadiness(): Promise<DiagnosticCheck> {
  const startedAt = Date.now();
  const config = resolveEmailConfig();

  if (config.provider !== 'smtp') {
    return {
      name: 'smtp_relay',
      status: 'pass',
      durationMs: Date.now() - startedAt,
      summary: `SMTP relay check skipped because EMAIL_PROVIDER=${config.provider}`,
      details: {
        provider: config.provider,
      },
    };
  }

  if (!config.smtpHost) {
    return {
      name: 'smtp_relay',
      status: 'fail',
      durationMs: Date.now() - startedAt,
      summary: 'SMTP relay is not configured',
      details: {
        provider: config.provider,
      },
    };
  }

  const smtpPort = config.smtpPort ?? 587;
  const mailpitUiUrl = process.env.MAILPIT_UI_URL?.trim();

  try {
    await tcpConnect(config.smtpHost, smtpPort, 5_000);

    let uiReachable: boolean | undefined;
    if (mailpitUiUrl) {
      const response = await fetch(mailpitUiUrl, { method: 'GET' });
      uiReachable = response.ok;
    }

    return {
      name: 'smtp_relay',
      status: checkStatus(uiReachable ?? true),
      durationMs: Date.now() - startedAt,
      summary:
        uiReachable === false
          ? 'SMTP relay port is reachable but Mailpit UI check failed'
          : 'SMTP relay is reachable',
      details: {
        provider: config.provider,
        host: config.smtpHost,
        port: smtpPort,
        ...(mailpitUiUrl ? { mailpitUiUrl, uiReachable } : {}),
      },
    };
  } catch (error) {
    return {
      name: 'smtp_relay',
      status: 'fail',
      durationMs: Date.now() - startedAt,
      summary: 'SMTP relay readiness check failed',
      error: error instanceof Error ? error.message : String(error),
      details: {
        provider: config.provider,
        host: config.smtpHost,
        port: smtpPort,
        ...(mailpitUiUrl ? { mailpitUiUrl } : {}),
      },
    };
  }
}

export async function runSmtpDeliverySelfTest(): Promise<DiagnosticCheck> {
  const startedAt = Date.now();
  const config = resolveEmailConfig();

  if (config.provider !== 'smtp') {
    return {
      name: 'smtp_delivery_self_test',
      status: 'pass',
      durationMs: Date.now() - startedAt,
      summary: `SMTP delivery self-test skipped because EMAIL_PROVIDER=${config.provider}`,
      details: {
        provider: config.provider,
      },
    };
  }

  const recipient = `self-test-${randomUUID()}@bytelyst.local`;
  const subject = `platform-service smtp self-test ${new Date().toISOString()}`;

  const result = await sendEmail(
    {
      to: recipient,
      from: config.fromEmail,
      fromName: config.fromName,
      subject,
      bodyHtml: '<p>SMTP self-test</p>',
      bodyText: 'SMTP self-test',
    },
    config,
    {
      info: () => undefined,
      error: () => undefined,
    }
  );

  return {
    name: 'smtp_delivery_self_test',
    status: checkStatus(result.success),
    durationMs: Date.now() - startedAt,
    summary: result.success
      ? 'SMTP delivery self-test email accepted by relay'
      : 'SMTP delivery self-test failed',
    error: result.error,
    details: {
      provider: config.provider,
      recipient,
      messageId: result.messageId,
      host: config.smtpHost,
      port: config.smtpPort ?? 587,
    },
  };
}

export async function getDependencyHealthReport(): Promise<DiagnosticsReport> {
  const [blobStorage, smtpRelay, serviceStatus] = await Promise.all([
    checkBlobStorageReadiness(),
    checkSmtpRelayReadiness(),
    checkAllServices(),
  ]);

  const dependencyChecks = [blobStorage, smtpRelay];

  return {
    status: reportStatus(dependencyChecks, serviceStatus),
    checkedAt: new Date().toISOString(),
    dependencyChecks,
    serviceStatus,
  };
}

export async function getSelfTestReport(): Promise<DiagnosticsReport> {
  const [blobStorage, smtpRelay, blobStorageSelfTest, smtpDeliverySelfTest, serviceStatus] =
    await Promise.all([
      checkBlobStorageReadiness(),
      checkSmtpRelayReadiness(),
      runBlobStorageSelfTest(),
      runSmtpDeliverySelfTest(),
      checkAllServices(),
    ]);

  const dependencyChecks = [blobStorage, smtpRelay, blobStorageSelfTest, smtpDeliverySelfTest];

  return {
    status: reportStatus(dependencyChecks, serviceStatus),
    checkedAt: new Date().toISOString(),
    dependencyChecks,
    serviceStatus,
  };
}
