import type {
  CsvParsedFile,
  CsvPreviewBatchIndex,
  CsvPreviewError,
  CsvPreviewRow,
  CsvPreviewWarning
} from './types';
import { validateCsvHeaders } from './header-validator';
import { resolveCsvRowDependencies } from './dependency-resolver';
import { resolveRowStatus } from './preview-builder';
import { normalizeEmail, normalizeLower, normalizeOrganizationCode } from './value-validator';
import { validateCsvRowValues } from './value-validator';

function key(...parts: Array<string | number | null | undefined>): string {
  return parts.map((part) => String(part ?? '').trim().toLowerCase()).join('|');
}

function issue(
  file: string,
  row: number,
  column: string,
  code: string,
  message: string,
  suggestedFix: string
): CsvPreviewError {
  return { file, row, column, code, message, suggestedFix };
}

function organizationCode(values: Record<string, string>): string {
  return values.organization_code ? normalizeOrganizationCode(values.organization_code) : '';
}

function businessKey(file: CsvParsedFile, values: Record<string, string>): string {
  const organization = organizationCode(values);

  switch (file.template.file) {
    case 'users.csv':
      return key(normalizeEmail(values.email));
    case 'organizations.csv':
      return key(organization);
    case 'memberships.csv':
      return key(organization, values.user_email);
    case 'crm-role-assignments.csv':
      return key(organization, values.user_email, values.crm_role_code);
    case 'master-options.csv':
      return key(organization, values.category, values.code);
    case 'branches.csv':
      return key(organization, values.branch_code);
    case 'product-types.csv':
      return key(organization, values.product_type_code);
    case 'document-sequences.csv':
      return key(
        organization,
        values.branch_code,
        values.product_type_code || 'all',
        values.document_type,
        values.period
      );
    case 'approval-workflows.csv':
      return key(organization, values.workflow_code);
    case 'approval-steps.csv':
      return key(organization, values.workflow_code, values.step_number);
    case 'approval-matrix.csv':
      return key(
        organization,
        values.entity_type,
        values.workflow_code,
        values.branch_code,
        values.product_type_code,
        values.min_amount,
        values.max_amount,
        values.priority
      );
    case 'customers.csv':
      return key(organization, values.customer_code);
    case 'contacts.csv':
      return values.email
        ? key(organization, values.customer_code, values.email)
        : key(organization, values.customer_code, values.contact_name);
    case 'contact-shares.csv':
      return key(organization, values.customer_code, values.contact_email, values.shared_to_user_email);
    case 'leads.csv':
      return key(organization, values.lead_code);
    case 'opportunities.csv':
      return key(organization, values.opportunity_code);
    case 'opportunity-parties.csv':
      return key(organization, values.opportunity_code, values.customer_code, values.role_code);
    case 'quotations.csv':
      return key(organization, values.quotation_code);
    case 'quotation-items.csv':
      return key(organization, values.quotation_code, values.item_number);
    case 'quotation-parties.csv':
      return key(organization, values.quotation_code, values.customer_code, values.role_code);
    case 'quotation-topics.csv':
      return key(organization, values.quotation_code, values.topic_key);
    case 'quotation-topic-items.csv':
      return key(organization, values.quotation_code, values.topic_key, values.sort_order, values.content);
    default:
      return key(organization, JSON.stringify(values));
  }
}

function normalizeRowValues(values: Record<string, string>): Record<string, string> {
  const normalized = { ...values };

  Object.keys(normalized).forEach((column) => {
    if (column.endsWith('_email') || column === 'email') normalized[column] = normalizeEmail(normalized[column]);
    if (
      column.endsWith('_code') ||
      [
        'slug',
        'category',
        'system_role',
        'membership_role',
        'business_role',
        'crm_role_code',
        'entity_type',
        'status',
        'priority',
        'quotation_type',
        'currency',
        'discount_type',
        'topic_type',
        'role_code'
      ].includes(column)
    ) {
      normalized[column] = normalizeLower(normalized[column]);
    }
    if (column === 'organization_code') normalized[column] = normalizeOrganizationCode(normalized[column]);
  });

  return normalized;
}

function mapFileLevelIssues(file: CsvParsedFile): CsvPreviewRow[] {
  const issues = [...file.errors, ...file.warnings];
  if (issues.length === 0) return [];

  const errors = issues.filter((item) =>
    ['INVALID_ENCODING', 'EMPTY_FILE', 'INCONSISTENT_COLUMN_COUNT'].includes(item.code)
  );
  const warnings = issues.filter((item) => !errors.includes(item));
  const row: CsvPreviewRow = {
    row: 1,
    status: errors.length > 0 ? 'FAIL' : 'WARNING',
    action: errors.length > 0 ? 'error' : 'skip',
    key: null,
    values: {},
    errors,
    warnings,
  };

  return [row];
}

export async function validateCsvPreviewFile(
  file: CsvParsedFile,
  batch: CsvPreviewBatchIndex
): Promise<CsvPreviewRow[]> {
  const headerErrors = validateCsvHeaders(file);
  if (file.errors.length > 0 || headerErrors.length > 0) {
    const issueRows = mapFileLevelIssues(file);
    if (headerErrors.length === 0) return issueRows;
    return [
      ...issueRows,
      {
        row: 1,
        status: 'FAIL',
        action: 'error',
        key: null,
        values: {},
        errors: headerErrors,
        warnings: []
      }
    ];
  }

  const seenBusinessKeys = new Map<string, number>();
  const previewRows: CsvPreviewRow[] = [];

  for (const row of file.rows) {
    const values = normalizeRowValues(row.values);
    const rowForValidation = { ...row, values };
    const valueValidation = validateCsvRowValues(file, rowForValidation);
    const dependencyValidation = await resolveCsvRowDependencies(file, rowForValidation, batch);
    const errors: CsvPreviewError[] = [...valueValidation.errors, ...dependencyValidation.errors];
    const warnings: CsvPreviewWarning[] = [...valueValidation.warnings, ...dependencyValidation.warnings];
    const keyValue = businessKey(file, values);
    const duplicateFirstRow = seenBusinessKeys.get(keyValue);

    if (duplicateFirstRow) {
      errors.push(
        issue(
          file.fileName,
          row.rowNumber,
          '__row__',
          'DUPLICATE_KEY',
          `Business key duplicates row ${duplicateFirstRow}.`,
          'Keep only one row for each import business key.'
        )
      );
    } else {
      seenBusinessKeys.set(keyValue, row.rowNumber);
    }

    const action = errors.length > 0 ? 'error' : dependencyValidation.exists ? 'update' : 'create';
    const previewRow: CsvPreviewRow = {
      row: row.rowNumber,
      status: 'PASS',
      action,
      key: keyValue,
      values,
      errors,
      warnings
    };
    previewRow.status = resolveRowStatus(previewRow);
    previewRows.push(previewRow);
  }

  return previewRows;
}
