import React from 'react';
import axios from 'axios';
import {
  GridColDef,
  GridRowParams,
  GridRenderCellParams,
  GridSingleSelectColDef,
} from '@mui/x-data-grid';
import dataFormatter from '../../helpers/dataFormatter';
import DataGridMultiSelect from '../DataGridMultiSelect';
import ListActionsPopover from '../ListActionsPopover';
import { hasPermission } from '../../helpers/userPermissions';
import { logger } from '../../lib/logger';

export interface ColumnMetadata {
  field: string;
  headerName: string;
  type?:
    | 'text'
    | 'boolean'
    | 'date'
    | 'datetime'
    | 'number'
    | 'relation'
    | 'relationMany'
    | 'singleSelectRelation'
    | 'image'
    | 'actions';
  editable?: boolean;
  sortable?: boolean;
  width?: number;
  flex?: number;
  minWidth?: number;
  entityRef?: string;
  displayField?: string;
  renderCell?: (params: GridRenderCellParams) => React.ReactElement;
  valueFormatter?: (value: unknown) => string;
}

export interface ColumnBuilderConfig {
  entityName: string;
  entityPath?: string;
  columns: ColumnMetadata[];
  updatePermission?: string;
}

const DEFAULT_COLUMN_PROPS = {
  flex: 1,
  minWidth: 120,
  filterable: false,
  headerClassName: 'datagrid--header',
  cellClassName: 'datagrid--cell',
};

async function fetchRelationOptions(
  entityRef: string,
  user: unknown,
): Promise<Array<{ id: string; label: string }>> {
  const permissionKey = `READ_${entityRef.toUpperCase()}`;
  if (!hasPermission(user, permissionKey)) {
    return [];
  }

  try {
    const response = await axios(`/${entityRef}/autocomplete?limit=100`);
    return response.data;
  } catch (error) {
    logger.error(
      `Failed to fetch ${entityRef} options`,
      error instanceof Error ? error : { error },
    );
    return [];
  }
}

function getFormatter(
  col: ColumnMetadata,
): ((params: { value: unknown }) => string) | undefined {
  if (col.valueFormatter) {
    const customFormatter = col.valueFormatter;
    return ({ value }) => customFormatter(value);
  }

  switch (col.type) {
    case 'boolean':
      return ({ value }) => dataFormatter.booleanFormatter(value);
    case 'date':
      return ({ value }) => dataFormatter.dateFormatter(value);
    case 'datetime':
      return ({ value }) => dataFormatter.dateTimeFormatter(value);
    case 'relation':
      return ({ value }) => {
        const formatter =
          dataFormatter[
            `${col.entityRef}OneListFormatter` as keyof typeof dataFormatter
          ];
        return typeof formatter === 'function'
          ? formatter(value)
          : String(value || '');
      };
    case 'relationMany':
      return ({ value }) => {
        const formatter =
          dataFormatter[
            `${col.entityRef}ManyListFormatter` as keyof typeof dataFormatter
          ];
        if (typeof formatter === 'function') {
          const result = formatter(value);
          return Array.isArray(result)
            ? result.join(', ')
            : String(result || '');
        }
        return Array.isArray(value)
          ? value.map((v: { name?: string }) => v?.name || '').join(', ')
          : '';
      };
    default:
      return undefined;
  }
}

function buildColumn(
  col: ColumnMetadata,
  hasUpdatePermission: boolean,
  _entityPath: string,
  valueOptionsMap: Map<string, Array<{ id: string; label: string }>>,
): GridColDef {
  const baseColumn: GridColDef = {
    ...DEFAULT_COLUMN_PROPS,
    field: col.field,
    headerName: col.headerName,
    editable:
      col.editable !== undefined ? col.editable && hasUpdatePermission : false,
    sortable: col.sortable !== undefined ? col.sortable : true,
  };

  if (col.flex !== undefined) baseColumn.flex = col.flex;
  if (col.width !== undefined) baseColumn.width = col.width;
  if (col.minWidth !== undefined) baseColumn.minWidth = col.minWidth;

  const formatter = getFormatter(col);
  if (formatter) {
    baseColumn.valueFormatter = formatter;
  }

  if (col.renderCell) {
    baseColumn.renderCell = col.renderCell;
  }

  if (col.type === 'relation' || col.type === 'relationMany') {
    baseColumn.type = 'singleSelect' as const;
    baseColumn.sortable = false;
    if (col.entityRef) {
      const entityRef = col.entityRef;
      baseColumn.renderEditCell = (params) => (
        <DataGridMultiSelect {...params} entityName={entityRef} />
      );
    }
  }

  if (col.type === 'singleSelectRelation' && col.entityRef) {
    const singleSelectColumn = baseColumn as GridSingleSelectColDef;
    singleSelectColumn.type = 'singleSelect';
    singleSelectColumn.sortable = false;
    singleSelectColumn.getOptionValue = (value: { id?: string }) => value?.id;
    singleSelectColumn.getOptionLabel = (value: { label?: string }) =>
      value?.label;
    singleSelectColumn.valueOptions = valueOptionsMap.get(col.entityRef) || [];
    singleSelectColumn.valueGetter = (value: { id?: string } | string | null) =>
      (typeof value === 'object' && value !== null ? value?.id : value) ??
      value;
    return singleSelectColumn;
  }

  if (col.type === 'datetime') {
    baseColumn.type = 'dateTime' as const;
    baseColumn.valueGetter = (_value, row) => new Date(row[col.field]);
  }

  if (col.type === 'number') {
    baseColumn.type = 'number';
  }

  if (col.type === 'boolean') {
    baseColumn.type = 'boolean';
  }

  if (col.type === 'image') {
    baseColumn.sortable = false;
    baseColumn.renderCell =
      col.renderCell ||
      ((params: GridRenderCellParams) => {
        const imageUrl =
          params.value &&
          Array.isArray(params.value) &&
          params.value[0]?.publicUrl;
        return imageUrl ? (
          <span
            role='img'
            aria-label='thumbnail'
            style={{
              display: 'inline-block',
              height: 40,
              width: 40,
              backgroundImage: `url(${imageUrl})`,
              backgroundSize: 'cover',
              backgroundPosition: 'center',
              borderRadius: 4,
            }}
          />
        ) : null;
      });
  }

  return baseColumn;
}

function buildActionsColumn(
  onDelete: (id: string) => void,
  entityPath: string,
  hasUpdatePermission: boolean,
): GridColDef {
  return {
    field: 'actions',
    type: 'actions' as const,
    minWidth: 30,
    headerClassName: 'datagrid--header',
    cellClassName: 'datagrid--cell',
    getActions: (params: GridRowParams) => [
      <div key={params?.row?.id}>
        <ListActionsPopover
          onDelete={onDelete}
          itemId={params?.row?.id}
          pathEdit={`/${entityPath}/${entityPath}-edit/?id=${params?.row?.id}`}
          pathView={`/${entityPath}/${entityPath}-view/?id=${params?.row?.id}`}
          hasUpdatePermission={hasUpdatePermission}
        />
      </div>,
    ],
  };
}

export async function buildColumns(
  config: ColumnBuilderConfig,
  onDelete: (id: string) => void,
  user: unknown,
): Promise<GridColDef[]> {
  const entityPath = config.entityPath || config.entityName;
  const updatePermission =
    config.updatePermission || `UPDATE_${config.entityName.toUpperCase()}`;
  const hasUpdatePermission = hasPermission(user, updatePermission);

  // Collect all singleSelectRelation entityRefs and fetch their options
  const singleSelectRelations = config.columns
    .filter((col) => col.type === 'singleSelectRelation' && col.entityRef)
    .map((col) => col.entityRef as string);

  const uniqueEntityRefs = Array.from(new Set(singleSelectRelations));
  const valueOptionsMap = new Map<
    string,
    Array<{ id: string; label: string }>
  >();

  await Promise.all(
    uniqueEntityRefs.map(async (entityRef) => {
      const options = await fetchRelationOptions(entityRef, user);
      valueOptionsMap.set(entityRef, options);
    }),
  );

  const dataColumns = config.columns
    .filter((col) => col.type !== 'actions')
    .map((col) =>
      buildColumn(col, hasUpdatePermission, entityPath, valueOptionsMap),
    );

  const hasActionsColumn = config.columns.some((col) => col.type === 'actions');

  if (hasActionsColumn) {
    const actionsColumn = buildActionsColumn(
      onDelete,
      entityPath,
      hasUpdatePermission,
    );
    return [...dataColumns, actionsColumn];
  }

  return dataColumns;
}

export function createColumnLoader(config: ColumnBuilderConfig) {
  return async (
    onDelete: (id: string) => void,
    _entityName: string,
    user: unknown,
  ): Promise<GridColDef[]> => {
    return buildColumns(config, onDelete, user);
  };
}

export default buildColumns;
