import 'server-only';

import { eq, inArray, sql } from 'drizzle-orm';
import {
  seedManifestItems,
  seedManifests,
  setupRuns,
  setupRunSteps
} from '@/db/schema';
import { db } from '@/lib/db';
import { hashPreviewPayload, getEnvironmentBlockers, validateConfirmationToken, withToken } from './reset-guards';
import {
  getConfirmationPhrase,
  ORGANIZATION_AWARE_RESET_SCOPES,
  RESET_SCOPES
} from './reset-scopes';
import type { ResetAffectedTable, ResetExecuteReport, ResetPreviewReport, ResetScope } from './reset-report';

export interface ResetPreviewInput {
  scope: ResetScope;
  organizationId?: string | null;
  organizationCode?: string | null;
  manifestId?: string | null;
  setupRunId?: string | null;
  options?: {
    force?: boolean;
    allowFullReset?: boolean;
  };
}

export interface ResetExecuteInput extends ResetPreviewInput {
  previewHash: string;
  confirmationToken: string;
  confirmationPhrase: string;
  actorId: string;
}

const BUSINESS_TABLES = [
  'crm_approval_actions',
  'crm_approval_requests',
  'crm_quotation_attachments',
  'crm_quotation_followups',
  'crm_quotation_topic_items',
  'crm_quotation_topics',
  'crm_quotation_customers',
  'crm_quotation_items',
  'crm_quotations',
  'crm_opportunity_attachments',
  'crm_opportunity_customers',
  'crm_opportunity_followups',
  'crm_opportunities',
  'crm_contact_shares',
  'crm_customer_contacts',
  'crm_customer_owner_history',
  'crm_leads',
  'crm_customers'
] as const;

const FOUNDATION_TABLES = [
  'crm_approval_matrices',
  'crm_approval_steps',
  'crm_approval_workflows',
  'document_sequences',
  'ms_options'
] as const;

function isResetScope(value: string): value is ResetScope {
  return RESET_SCOPES.includes(value as ResetScope);
}

async function countRows(table: string, organizationId?: string | null) {
  const result = await db.execute(
    organizationId
      ? sql`select count(*)::int as count from ${sql.raw(table)} where organization_id = ${organizationId}`
      : sql`select count(*)::int as count from ${sql.raw(table)}`
  );
  const row = result[0] as { count?: number | string } | undefined;
  return Number(row?.count ?? 0);
}

async function countRowsByColumn(table: string, column: string, value: string) {
  const result = await db.execute(
    sql`select count(*)::int as count from ${sql.raw(table)} where ${sql.raw(column)} = ${value}`
  );
  const row = result[0] as { count?: number | string } | undefined;
  return Number(row?.count ?? 0);
}

async function countSeedManifestItemsForRun(setupRunId: string) {
  const manifests = await db.select().from(seedManifests).where(eq(seedManifests.setupRunId, setupRunId));
  const manifestIds = manifests.map((manifest) => manifest.id);
  if (manifestIds.length === 0) return 0;

  const result = await db
    .select()
    .from(seedManifestItems)
    .where(inArray(seedManifestItems.seedManifestId, manifestIds));

  return result.length;
}

function buildSummary(affectedTables: ResetAffectedTable[], warnings: string[], errors: string[]) {
  return {
    tables: affectedTables.length,
    rows: affectedTables.reduce((total, table) => total + table.estimatedRows, 0),
    warnings: warnings.length,
    errors: errors.length
  };
}

async function getManifestImpact(input: ResetPreviewInput): Promise<ResetPreviewReport['manifestImpact']> {
  if (input.scope === 'setup_run_reset') {
    const manifests = input.setupRunId
      ? await db.select().from(seedManifests).where(eq(seedManifests.setupRunId, input.setupRunId))
      : await db.select().from(seedManifests);

    return manifests.map((manifest) => ({
      id: manifest.id,
      name: manifest.name,
      version: manifest.version,
      status: manifest.status,
      action: 'deleted'
    }));
  }

  if (input.scope === 'csv_import_rollback' && input.manifestId) {
    const manifests = await db.select().from(seedManifests).where(eq(seedManifests.id, input.manifestId));
    return manifests.map((manifest) => ({
      id: manifest.id,
      name: manifest.name,
      version: manifest.version,
      status: manifest.status,
      action: 'warning'
    }));
  }

  if (input.organizationId) {
    const manifests = await db
      .select()
      .from(seedManifests)
      .where(eq(seedManifests.organizationId, input.organizationId));

    return manifests.map((manifest) => ({
      id: manifest.id,
      name: manifest.name,
      version: manifest.version,
      status: manifest.status,
      action: input.scope === 'business_reset' ? 'rolled_back' : 'warning'
    }));
  }

  return [];
}

async function buildAffectedTables(input: ResetPreviewInput) {
  if (input.scope === 'business_reset' && input.organizationId) {
    return Promise.all(
      BUSINESS_TABLES.map(async (table) => ({
        table,
        estimatedRows: await countRows(table, input.organizationId),
        action: 'delete' as const,
        retainedData: 'Organization, users, memberships, foundation rows, and data outside target organization.',
        deletedData: `Business rows for organization ${input.organizationId}.`
      }))
    );
  }

  if (input.scope === 'foundation_reset' && input.organizationId) {
    return Promise.all(
      FOUNDATION_TABLES.map(async (table) => ({
        table,
        estimatedRows: await countRows(table, input.organizationId),
        action: 'blocked' as const,
        retainedData: 'Business data and users are retained.',
        deletedData: 'Foundation rows require follow-up service-specific reseed support before deletion.'
      }))
    );
  }

  if (input.scope === 'setup_run_reset') {
    const runCount = input.setupRunId
      ? await countRowsByColumn('setup_runs', 'id', input.setupRunId)
      : await countRows('setup_runs');
    return [
      {
        table: 'setup_runs',
        estimatedRows: runCount,
        action: 'delete' as const,
        retainedData: 'Business, organization, user, and foundation data.',
        deletedData: input.setupRunId ? `Setup run ${input.setupRunId}.` : 'All setup run metadata.'
      },
      {
        table: 'setup_run_steps',
        estimatedRows: input.setupRunId
          ? await countRowsByColumn('setup_run_steps', 'setup_run_id', input.setupRunId)
          : await countRows('setup_run_steps'),
        action: 'delete' as const,
        retainedData: 'Actual CRM and foundation data.',
        deletedData: 'Setup step reports.'
      },
      {
        table: 'seed_manifests',
        estimatedRows: input.setupRunId
          ? await countRowsByColumn('seed_manifests', 'setup_run_id', input.setupRunId)
          : await countRows('seed_manifests'),
        action: 'delete' as const,
        retainedData: 'Actual imported data remains untouched.',
        deletedData: 'Seed manifest metadata.'
      },
      {
        table: 'seed_manifest_items',
        estimatedRows: input.setupRunId
          ? await countSeedManifestItemsForRun(input.setupRunId)
          : await countRows('seed_manifest_items'),
        action: 'delete' as const,
        retainedData: 'Actual imported data remains untouched.',
        deletedData: 'Seed manifest item metadata.'
      }
    ];
  }

  return [];
}

export async function previewReset(input: ResetPreviewInput): Promise<ResetPreviewReport> {
  if (!isResetScope(input.scope)) {
    throw new Error('Unsupported reset scope.');
  }

  const warnings: string[] = [];
  const errors = getEnvironmentBlockers();

  if (ORGANIZATION_AWARE_RESET_SCOPES.has(input.scope) && !input.organizationId) {
    errors.push('Target organization is required for this reset scope.');
  }

  if (input.scope === 'full_reset') {
    errors.push('Full reset is not exposed through the dashboard API. Use the guarded npm reset script with explicit local confirmation.');
  }

  if (input.scope === 'demo_uat_reset') {
    errors.push('Demo/UAT reset requires persisted UAT seed ownership markers and is blocked until those markers are available.');
  }

  if (input.scope === 'csv_import_rollback') {
    if (!input.manifestId) {
      errors.push('CSV import rollback requires a seed manifest id.');
    }
    warnings.push('CSV import rollback is report-only unless manifest-owned rows are proven safely reversible.');
  }

  if (input.scope === 'foundation_reset') {
    warnings.push('Foundation reset is preview-only until service-specific reseed wrappers are available.');
  }

  const affectedTables = await buildAffectedTables(input);
  const manifestImpact = await getManifestImpact(input);
  const confirmationPhrase = getConfirmationPhrase({
    scope: input.scope,
    organizationCode: input.organizationCode ?? input.organizationId ?? null,
    manifestId: input.manifestId ?? null,
    setupRunId: input.setupRunId ?? null
  });
  const previewPayload = {
    scope: input.scope,
    organizationId: input.organizationId ?? null,
    manifestId: input.manifestId ?? null,
    affectedTables,
    manifestImpact,
    warnings,
    errors
  };
  const previewHash = hashPreviewPayload(previewPayload);

  return withToken<ResetPreviewReport>({
    scope: input.scope,
    targetOrganizationId: input.organizationId ?? null,
    targetOrganizationCode: input.organizationCode ?? null,
    manifestId: input.manifestId ?? null,
    summary: buildSummary(affectedTables, warnings, errors),
    affectedTables,
    retainedData: [...new Set(affectedTables.map((table) => table.retainedData))],
    deletedData: [...new Set(affectedTables.map((table) => table.deletedData))],
    storageCleanup: {
      mode: input.scope === 'business_reset' ? 'best_effort' : 'none',
      plannedKeys: [],
      warnings:
        input.scope === 'business_reset'
          ? ['Storage cleanup is best-effort and limited to setup-owned temporary or import-owned objects.']
          : []
    },
    manifestImpact,
    warnings,
    errors,
    confirmationPhrase,
    previewHash
  });
}

async function executeSetupRunReset(input: ResetExecuteInput) {
  await db.transaction(async (tx) => {
    if (input.setupRunId) {
      const manifests = await tx
        .select()
        .from(seedManifests)
        .where(eq(seedManifests.setupRunId, input.setupRunId));
      const manifestIds = manifests.map((manifest) => manifest.id);
      if (manifestIds.length > 0) {
        await tx.delete(seedManifestItems).where(inArray(seedManifestItems.seedManifestId, manifestIds));
      }
      await tx.delete(seedManifests).where(eq(seedManifests.setupRunId, input.setupRunId));
      await tx.delete(setupRunSteps).where(eq(setupRunSteps.setupRunId, input.setupRunId));
      await tx.delete(setupRuns).where(eq(setupRuns.id, input.setupRunId));
      return;
    }

    await tx.delete(seedManifestItems);
    await tx.delete(seedManifests);
    await tx.delete(setupRunSteps);
    await tx.delete(setupRuns);
  });
}

async function executeBusinessReset(organizationId: string) {
  await db.transaction(async (tx) => {
    for (const table of BUSINESS_TABLES) {
      await tx.execute(sql`delete from ${sql.raw(table)} where organization_id = ${organizationId}`);
    }
    await tx
      .update(seedManifests)
      .set({ status: 'rolled_back', updatedAt: new Date() })
      .where(eq(seedManifests.organizationId, organizationId));
    await tx
      .update(setupRunSteps)
      .set({ status: 'stale', updatedAt: new Date() })
      .where(inArray(setupRunSteps.stepKey, ['csv_commit', 'verification']));
  });
}

export async function executeReset(input: ResetExecuteInput): Promise<ResetExecuteReport> {
  const preview = await previewReset(input);
  const tokenError = validateConfirmationToken({
    token: input.confirmationToken,
    kind: 'reset',
    scope: input.scope,
    previewHash: input.previewHash
  });
  const errors = [...preview.errors];

  if (preview.previewHash !== input.previewHash) {
    errors.push('Preview hash mismatch. Re-run reset preview.');
  }
  if (tokenError) errors.push(tokenError);
  if (input.confirmationPhrase !== preview.confirmationPhrase) {
    errors.push('Typed confirmation phrase does not match preview.');
  }

  if (errors.length > 0) {
    return { ...preview, errors, summary: { ...preview.summary, errors: errors.length }, executedAt: new Date().toISOString(), executed: false, deletedRows: 0, updatedManifests: 0, storageCleanupResult: { deletedKeys: [], leftoverKeys: [], warnings: [] } };
  }

  const beforeRows = preview.summary.rows;

  if (input.scope === 'setup_run_reset') {
    await executeSetupRunReset(input);
  } else if (input.scope === 'business_reset' && input.organizationId) {
    await executeBusinessReset(input.organizationId);
  } else {
    return {
      ...preview,
      errors: ['This reset scope is preview-only until a reversible, service-specific implementation is available.'],
      summary: { ...preview.summary, errors: preview.summary.errors + 1 },
      executedAt: new Date().toISOString(),
      executed: false,
      deletedRows: 0,
      updatedManifests: 0,
      storageCleanupResult: { deletedKeys: [], leftoverKeys: [], warnings: [] }
    };
  }

  return {
    ...preview,
    executedAt: new Date().toISOString(),
    executed: true,
    deletedRows: beforeRows,
    updatedManifests: preview.manifestImpact.length,
    storageCleanupResult: {
      deletedKeys: [],
      leftoverKeys: [],
      warnings: preview.storageCleanup.warnings
    }
  };
}
