import React from 'react';
import dayjs from 'dayjs';

const EMPTY_VALUE = 'Not yet recorded';
const SYSTEM_FIELDS = new Set([
  'id',
  'createdAt',
  'updatedAt',
  'deletedAt',
  'created_at',
  'updated_at',
  'deleted_at',
  'password',
  'passwordHash',
  'salt',
]);

const PRIMARY_TITLE_FIELDS = [
  'name',
  'title',
  'label',
  'subject',
  'project_name',
  'program_name',
  'contract_title',
  'contract_number',
  'code',
  'project_code',
  'email',
  'firstName',
  'lastName',
];

const SUMMARY_FIELD_PRIORITY = [
  'name',
  'title',
  'label',
  'code',
  'project_code',
  'contract_number',
  'status',
  'state',
  'phase',
  'priority',
  'risk_level',
  'type',
  'category',
  'organization',
  'program',
  'project',
  'vendor',
  'department',
  'assigned_to',
  'requested_by',
  'requested_at',
  'due_date',
  'start_date',
  'end_date',
  'amount',
  'budget_amount',
  'contract_value',
  'currency',
];

const CHIP_FIELDS = ['status', 'state', 'phase', 'priority', 'risk_level', 'type', 'category'];

const DATE_SUFFIXES = ['_at', '_date'];
const DATE_KEYWORDS = ['date', 'time', 'deadline'];
const AMOUNT_KEYWORDS = ['amount', 'value', 'cost', 'budget', 'total', 'price', 'percent'];

export const emptyValue = EMPTY_VALUE;

export const titleCase = (value?: string | null) => {
  if (!value) {
    return '';
  }

  return value
    .replace(/_/g, ' ')
    .replace(/\b\w/g, (match) => match.toUpperCase());
};

export const singularizeLabel = (value: string) => {
  if (value.endsWith('ies')) {
    return `${value.slice(0, -3)}y`;
  }

  if (value.endsWith('ches') || value.endsWith('shes') || value.endsWith('xes') || value.endsWith('zes')) {
    return value.slice(0, -2);
  }

  if (value.endsWith('ses') && !value.endsWith('issues') && !value.endsWith('buses')) {
    return value.slice(0, -1);
  }

  if (value.endsWith('s')) {
    return value.slice(0, -1);
  }

  return value;
};

export const humanizeLabel = (key: string) => {
  return titleCase(
    key
      .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
      .replace(/_/g, ' ')
      .replace(/\bid\b/i, 'ID')
      .trim(),
  );
};

export const isRecordObject = (value: any) => {
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date);
};

export const isDateLikeKey = (key: string) => {
  return DATE_SUFFIXES.some((suffix) => key.endsWith(suffix)) || DATE_KEYWORDS.some((part) => key.includes(part));
};

export const isAmountLikeKey = (key: string) => {
  return AMOUNT_KEYWORDS.some((part) => key.includes(part));
};

export const stripHtml = (value: string) => value.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();

export const formatPrimitiveValue = (key: string, value: any): React.ReactNode => {
  if (value === null || value === undefined || value === '') {
    return EMPTY_VALUE;
  }

  if (typeof value === 'boolean') {
    return value ? 'Yes' : 'No';
  }

  if (typeof value === 'number') {
    if (key.toLowerCase().includes('percent')) {
      return `${value}%`;
    }

    return value.toLocaleString();
  }

  if (value instanceof Date || (typeof value === 'string' && isDateLikeKey(key) && dayjs(value).isValid())) {
    return dayjs(value).format('DD MMM YYYY HH:mm');
  }

  if (typeof value === 'string') {
    if (value.trim().startsWith('<') && value.includes('>')) {
      const stripped = stripHtml(value);
      return stripped || EMPTY_VALUE;
    }

    return value;
  }

  return String(value);
};

export const getRelationLabel = (value: any) => {
  if (!isRecordObject(value)) {
    return EMPTY_VALUE;
  }

  const candidates = ['name', 'title', 'label', 'code', 'project_code', 'contract_number', 'email', 'id'];
  for (const candidate of candidates) {
    if (value[candidate]) {
      return formatPrimitiveValue(candidate, value[candidate]);
    }
  }

  const visibleEntry = Object.entries(value).find(([entryKey, entryValue]) => !SYSTEM_FIELDS.has(entryKey) && !isRecordObject(entryValue) && !Array.isArray(entryValue));
  return visibleEntry ? formatPrimitiveValue(visibleEntry[0], visibleEntry[1]) : EMPTY_VALUE;
};

export const getPrimaryTitle = (record: any, fallback: string) => {
  if (!isRecordObject(record)) {
    return fallback;
  }

  const firstName = record.firstName || record.first_name;
  const lastName = record.lastName || record.last_name;
  if (firstName || lastName) {
    return [firstName, lastName].filter(Boolean).join(' ');
  }

  for (const field of PRIMARY_TITLE_FIELDS) {
    if (record[field]) {
      const value = formatPrimitiveValue(field, record[field]);
      return typeof value === 'string' ? value : fallback;
    }
  }

  return fallback;
};

export const getRecordSubtitle = (record: any, fallback: string) => {
  if (!isRecordObject(record)) {
    return fallback;
  }

  const referenceFields = ['code', 'project_code', 'contract_number', 'email', 'id'];
  const reference = referenceFields.find((field) => record[field]);
  if (!reference) {
    return fallback;
  }

  return `${humanizeLabel(reference)}: ${formatPrimitiveValue(reference, record[reference])}`;
};

export const getRecordEntries = (record: any) => {
  if (!isRecordObject(record)) {
    return { scalarEntries: [], relationEntries: [], collectionEntries: [] };
  }

  const scalarEntries = Object.entries(record).filter(([key, value]) => {
    return !SYSTEM_FIELDS.has(key) && !Array.isArray(value) && !isRecordObject(value);
  });

  const relationEntries = Object.entries(record).filter(([key, value]) => {
    return !SYSTEM_FIELDS.has(key) && isRecordObject(value);
  });

  const collectionEntries = Object.entries(record).filter(([key, value]) => {
    return !SYSTEM_FIELDS.has(key) && Array.isArray(value);
  });

  return { scalarEntries, relationEntries, collectionEntries };
};

export const sortEntries = (entries: Array<[string, any]>) => {
  return [...entries].sort((left, right) => {
    const leftIndex = SUMMARY_FIELD_PRIORITY.indexOf(left[0]);
    const rightIndex = SUMMARY_FIELD_PRIORITY.indexOf(right[0]);

    if (leftIndex !== -1 || rightIndex !== -1) {
      return (leftIndex === -1 ? 999 : leftIndex) - (rightIndex === -1 ? 999 : rightIndex);
    }

    return humanizeLabel(left[0]).localeCompare(humanizeLabel(right[0]));
  });
};

export const getSummaryEntries = (record: any, limit = 8) => {
  const { scalarEntries, relationEntries } = getRecordEntries(record);
  const scalar = sortEntries(scalarEntries);
  const relation = sortEntries(relationEntries)
    .filter(([key]) => !CHIP_FIELDS.includes(key))
    .map(([key, value]) => [key, getRelationLabel(value)] as [string, React.ReactNode]);

  return [...scalar, ...relation].slice(0, limit);
};

export const getChipEntries = (record: any) => {
  if (!isRecordObject(record)) {
    return [] as Array<[string, any]>;
  }

  return CHIP_FIELDS.filter((field) => record[field]).map((field) => [field, record[field]] as [string, any]);
};

export const getMetricCards = (record: any) => {
  const { collectionEntries } = getRecordEntries(record);
  const metrics = [
    {
      label: 'Linked sections',
      value: collectionEntries.length.toString(),
      note: collectionEntries.length ? 'Related collections are shown below.' : 'No related collections are attached yet.',
    },
  ];

  const importantEntries = [
    ['status', record?.status],
    ['start_date', record?.start_date],
    ['end_date', record?.end_date],
    ['budget_amount', record?.budget_amount ?? record?.amount ?? record?.contract_value],
  ].filter(([, value]) => value !== undefined && value !== null && value !== '');

  for (const [key, value] of importantEntries.slice(0, 3)) {
    metrics.push({
      label: humanizeLabel(key),
      value: String(formatPrimitiveValue(key, value)),
      note: key === 'budget_amount' ? 'Tracked financial figure for this record.' : 'Key operating signal for this record.',
    });
  }

  return metrics.slice(0, 4);
};

export const getCollectionPreview = (items: any[]) => {
  return items.slice(0, 6).map((item, index) => {
    if (!isRecordObject(item)) {
      return {
        key: String(index),
        title: formatPrimitiveValue('value', item),
        details: [],
      };
    }

    const title = getPrimaryTitle(item, `Record ${index + 1}`);
    const details = sortEntries(
      Object.entries(item).filter(([key, value]) => !SYSTEM_FIELDS.has(key) && !Array.isArray(value) && !isRecordObject(value)),
    )
      .filter(([key]) => !PRIMARY_TITLE_FIELDS.includes(key))
      .slice(0, 4)
      .map(([key, value]) => ({
        label: humanizeLabel(key),
        value: formatPrimitiveValue(key, value),
      }));

    return {
      key: String(item.id || index),
      title,
      details,
    };
  });
};
