import React, { useEffect, useMemo, useState } from 'react';
import { Button, Input, Paragraph, ScrollView, Separator, Spinner, Text, XStack, YStack } from 'tamagui';
import { getIcon } from './IconMapper.jsx';
import { normalizeColumnsArray } from './grid/utils.js';
import { getTypographyRoleProps } from '../styles/index.js';

const EMPTY_COLUMNS = [];
const EMPTY_ACTIONS = [];
const DEFAULT_SEARCH_CONFIG = { enabled: true, placeholder: 'Search records...' };
const EMPTY_SUMMARY_DEFINITIONS = [];

function getColumnJustify(align) {
  if (align === 'right') {
    return 'flex-end';
  }
  if (align === 'center') {
    return 'center';
  }
  return 'flex-start';
}

function normalizeSummaryValue(value) {
  if (typeof value === 'number') {
    return Number.isInteger(value) ? String(value) : value.toFixed(2);
  }
  if (value && typeof value === 'object') {
    return Object.entries(value)
      .map(([key, count]) => `${key}: ${count}`)
      .join(' · ');
  }
  return String(value ?? '');
}

function SummaryCards({ summary }) {
  if (!summary?.items?.length) {
    return null;
  }

  return (
    <XStack gap="$3" flexWrap="wrap">
      {summary.items.map((item) => (
        <YStack
          key={item.id || item.label}
          minWidth={140}
          padding="$3"
          borderWidth={1}
          borderColor="$lineSubtle"
          borderRadius="$radiusMd"
          backgroundColor="$bgPanel"
          gap="$1"
        >
          <Text fontSize="$3" color="$textMuted">
            {item.label}
          </Text>
          <Text fontSize="$7" fontWeight="700" color="$accent">
            {normalizeSummaryValue(item.value)}
          </Text>
        </YStack>
      ))}
    </XStack>
  );
}

function HeaderCell({ column, orderBy, order, onSort }) {
  const sortable = column.sortable !== false;
  const isActive = orderBy === column.id;
  const CaretUp = getIcon('caret-up');
  const CaretDown = getIcon('caret-down');
  const justifyContent = getColumnJustify(column.align);

  return (
    <XStack
      flex={column.flex || 1}
      flexBasis={0}
      minWidth={column.minWidth || 120}
      alignItems="center"
      justifyContent={justifyContent}
    >
      <Button
        chromeless
        disabled={!sortable}
        onPress={() => sortable && onSort(column.id)}
        padding={0}
        justifyContent={justifyContent}
        width="100%"
        hoverStyle={sortable ? { backgroundColor: '$bgPage' } : undefined}
        pressStyle={sortable ? { backgroundColor: '$bgPanelElev' } : undefined}
      >
        <XStack width="100%" alignItems="center" justifyContent={justifyContent} gap="$2">
          <Text {...getTypographyRoleProps('tableHeader')} textAlign={column.align || 'left'} numberOfLines={1}>
            {column.label}
          </Text>
          {sortable ? (
            isActive ? (
              order === 'asc'
                ? (CaretUp ? <CaretUp size="xs" color="$textSecondary" /> : null)
                : (CaretDown ? <CaretDown size="xs" color="$textSecondary" /> : null)
            ) : (
              CaretDown ? <CaretDown size="xs" color="$textMuted" style={{ opacity: 0.6 }} /> : null
            )
          ) : null}
        </XStack>
      </Button>
    </XStack>
  );
}

function RowCell({ column, record }) {
  const value = record?.[column.id];
  const content = column.render ? column.render(value, record, column) : (value ?? '');
  const justifyContent = getColumnJustify(column.align);
  return (
    <XStack
      flex={column.flex || 1}
      flexBasis={0}
      minWidth={column.minWidth || 120}
      justifyContent={justifyContent}
      alignItems="center"
    >
      <XStack width="100%" justifyContent={justifyContent} alignItems="center">
        {React.isValidElement(content) ? content : (
          <Text color="$color" numberOfLines={1} textAlign={column.align || 'left'} width="100%">
            {String(content)}
          </Text>
        )}
      </XStack>
    </XStack>
  );
}

export function DirView({
  dataModel,
  columns = EMPTY_COLUMNS,
  title = 'Directory',
  toolbarActions = EMPTY_ACTIONS,
  toolbarItems = undefined,
  actions = undefined,
  topLeftContent = null,
  topRightContent = null,
  bodyHeaderContent = null,
  bodyFooterContent = null,
  searchConfig = DEFAULT_SEARCH_CONFIG,
  searchValue = undefined,
  onSearchChange = null,
  initialSearchValue = '',
  summaryDefinitions = EMPTY_SUMMARY_DEFINITIONS,
  pageSize = 10,
  bodyMaxHeight = 480,
  onRowClick = null,
  onRowPress = null,
  onRefresh = null,
  showHeader = true,
  showSummary = false,
  density = 'comfortable',
  striped = false
}) {
  const [dataVersion, setDataVersion] = useState(0);
  const [records, setRecords] = useState([]);
  const [summary, setSummary] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');
  const [internalSearchTerm, setInternalSearchTerm] = useState(initialSearchValue);
  const resolvedColumns = useMemo(() => normalizeColumnsArray(columns), [columns]);
  const effectiveToolbarItems = actions ?? toolbarItems ?? toolbarActions;
  const effectiveSearchTerm = searchValue ?? internalSearchTerm;
  const effectiveRowPress = onRowPress ?? onRowClick;
  const [orderBy, setOrderBy] = useState(resolvedColumns.find((column) => column.sortable !== false)?.id || '');
  const [order, setOrder] = useState('asc');
  const [currentPage, setCurrentPage] = useState(1);
  const [totalRecords, setTotalRecords] = useState(0);
  const resolvedSummaryDefinitions = summaryDefinitions || EMPTY_SUMMARY_DEFINITIONS;
  const RefreshIcon = getIcon('refresh');
  const FirstPageIcon = getIcon('first-page');
  const PreviousPageIcon = getIcon('chevron-left');
  const NextPageIcon = getIcon('chevron-right');
  const LastPageIcon = getIcon('last-page');
  const paddingRow = density === 'compact' ? '$2' : density === 'spacious' ? '$4' : '$3';

  useEffect(() => {
    if (!dataModel?.subscribe) {
      return undefined;
    }

    return dataModel.subscribe(() => {
      setDataVersion((value) => value + 1);
    });
  }, [dataModel]);

  const totalPages = useMemo(() => Math.max(1, Math.ceil(totalRecords / pageSize)), [totalRecords, pageSize]);

  useEffect(() => {
    let cancelled = false;

    async function loadData() {
      if (!dataModel) {
        return;
      }

      setLoading(true);
      setError('');
      try {
        const offset = (currentPage - 1) * pageSize;
        const sortBy = orderBy ? [{ field: orderBy, direction: order }] : [];
        const filterBy = effectiveSearchTerm ? { search: effectiveSearchTerm } : {};
        const query = {
          offset,
          page_size: pageSize,
          sort_by: sortBy,
          filter_by: filterBy
        };

        const [recordResult, summaryResult] = await Promise.all([
          dataModel.queryRecords(query),
          dataModel.querySummary(query, resolvedSummaryDefinitions)
        ]);

        if (!cancelled) {
          setRecords(recordResult.rows || []);
          setTotalRecords(recordResult.total || 0);
          setSummary(summaryResult || null);
        }
      } catch (loadError) {
        if (!cancelled) {
          setError(loadError.message || 'Failed to load records');
        }
      } finally {
        if (!cancelled) {
          setLoading(false);
        }
      }
    }

    loadData();
    return () => {
      cancelled = true;
    };
  }, [currentPage, dataModel, dataVersion, effectiveSearchTerm, order, orderBy, pageSize, resolvedSummaryDefinitions]);

  useEffect(() => {
    const nextOrderBy = resolvedColumns.find((column) => column.sortable !== false)?.id || '';
    setOrderBy((current) => {
      if (current && resolvedColumns.some((column) => column.id === current)) {
        return current;
      }
      return nextOrderBy;
    });
  }, [resolvedColumns]);

  const handleRefresh = async () => {
    if (onRefresh) {
      await onRefresh();
    }
    setDataVersion((value) => value + 1);
  };

  const updateSearchTerm = (value) => {
    if (searchValue === undefined) {
      setInternalSearchTerm(value);
    }
    onSearchChange?.(value);
    searchConfig?.onChange?.(value);
    setCurrentPage(1);
  };

  const renderToolbarButton = (action, index) => {
    if (React.isValidElement(action)) {
      return React.cloneElement(action, { key: action.key || `toolbar-${index}` });
    }

    if (action?.kind === 'text') {
      return (
        <Text key={action?.key || action?.text || index} color="$color" opacity={0.7}>
          {action?.text}
        </Text>
      );
    }

    if (action?.kind === 'node') {
      return <React.Fragment key={action?.key || index}>{action?.node}</React.Fragment>;
    }

    const IconComponent = action?.icon ? getIcon(action.icon) : null;
    return (
      <Button
        key={action?.id || action?.label || index}
        size="$3"
        theme={action?.theme}
        chromeless={action?.chromeless}
        disabled={loading || action?.disabled}
        icon={IconComponent ? <IconComponent size={16} /> : undefined}
        onPress={action?.onPress}
      >
        {action?.label}
      </Button>
    );
  };

  return (
    <YStack gap="$4" width="100%">
      {showHeader ? (
        <XStack justifyContent="space-between" alignItems="center" gap="$4" flexWrap="wrap">
          <XStack alignItems="center" gap="$3" flex={1} minWidth={240} flexWrap="wrap">
            <Text {...getTypographyRoleProps('sectionTitle')}>
              {title}
            </Text>
            {topLeftContent}
          </XStack>

          <XStack alignItems="center" justifyContent="flex-end" gap="$2" flexWrap="wrap">
            {searchConfig?.enabled ? (
              <Input
                width={260}
                placeholder={searchConfig.placeholder || 'Search records...'}
                value={effectiveSearchTerm}
                onChangeText={updateSearchTerm}
                backgroundColor="$bgPanel"
                borderColor="$lineSubtle"
                focusStyle={{ borderColor: '$accent' }}
              />
            ) : null}
            {effectiveToolbarItems.map(renderToolbarButton)}
            <Button
              size="$3"
              chromeless
              circular
              aria-label="Refresh directory"
              icon={RefreshIcon ? <RefreshIcon size="sm" color="$textSecondary" /> : undefined}
              onPress={handleRefresh}
              disabled={loading}
            />
            {topRightContent}
          </XStack>
        </XStack>
      ) : null}

      {error ? (
        <YStack padding="$3" borderRadius="$radiusMd" backgroundColor="$dangerBg" borderWidth={1} borderColor="$danger">
          <Text color="$danger" fontWeight="600">{error}</Text>
        </YStack>
      ) : null}

      {showSummary ? <SummaryCards summary={summary} /> : null}

      {bodyHeaderContent}

      <YStack borderWidth={1} borderColor="$lineSubtle" borderRadius="$radiusLg" overflow="hidden" backgroundColor="$bgPanel">
        <XStack padding={paddingRow} gap="$3" backgroundColor="transparent" borderBottomWidth={1} borderBottomColor="$lineSubtle">
          {resolvedColumns.map((column) => (
            <HeaderCell
              key={column.id}
              column={column}
              orderBy={orderBy}
              order={order}
              onSort={(columnId) => {
                const nextOrder = orderBy === columnId && order === 'asc' ? 'desc' : 'asc';
                setOrderBy(columnId);
                setOrder(nextOrder);
                setCurrentPage(1);
              }}
            />
          ))}
        </XStack>

        <ScrollView {...(bodyMaxHeight != null ? { maxHeight: bodyMaxHeight } : {})}>
          <YStack>
            {loading ? (
              <XStack justifyContent="center" padding="$6">
                <Spinner size="large" color="$accent" />
              </XStack>
            ) : records.length === 0 ? (
              <YStack padding="$6" alignItems="center" gap="$2">
                <Paragraph color="$textSecondary">
                  No records found
                </Paragraph>
                <Text color="$textMuted" fontSize="$3">Try adjusting your search or filters.</Text>
              </YStack>
            ) : (
              records.map((record, index) => (
                <YStack key={record?.[dataModel?.getIdField?.() || 'id'] || index}>
                  <XStack
                    padding={paddingRow}
                    gap="$3"
                    alignItems="center"
                    backgroundColor={striped && index % 2 === 1 ? '$bgPage' : 'transparent'}
                    hoverStyle={{ backgroundColor: '$bgPage' }}
                    pressStyle={{ backgroundColor: '$bgPanelElev' }}
                    cursor={effectiveRowPress ? 'pointer' : undefined}
                    onPress={() => effectiveRowPress?.(record)}
                  >
                    {resolvedColumns.map((column) => (
                      <RowCell key={column.id} column={column} record={record} />
                    ))}
                  </XStack>
                  {index < records.length - 1 ? <Separator borderColor="$lineSubtle" /> : null}
                </YStack>
              ))
            )}
          </YStack>
        </ScrollView>
      </YStack>

      {bodyFooterContent}

      <XStack justifyContent="space-between" alignItems="center" gap="$3" flexWrap="wrap">
        <Text color="$textMuted">
          Rows: {totalRecords}
        </Text>
        <XStack alignItems="center" gap="$2" flexWrap="wrap" padding="$1" borderWidth={1} borderColor="$lineSubtle" borderRadius="$radiusMd" backgroundColor="$bgPanel">
          <Button
            size="$3"
            chromeless
            aria-label="First page"
            icon={FirstPageIcon ? <FirstPageIcon size="sm" color="$textSecondary" /> : undefined}
            onPress={() => setCurrentPage(1)}
            disabled={currentPage === 1 || loading}
          />
          <Button
            size="$3"
            chromeless
            aria-label="Previous page"
            icon={PreviousPageIcon ? <PreviousPageIcon size="sm" color="$textSecondary" /> : undefined}
            onPress={() => setCurrentPage((value) => Math.max(1, value - 1))}
            disabled={currentPage === 1 || loading}
          />
          <Text color="$textSecondary">
            Page {currentPage} of {totalPages}
          </Text>
          <Button
            size="$3"
            chromeless
            aria-label="Next page"
            icon={NextPageIcon ? <NextPageIcon size="sm" color="$textSecondary" /> : undefined}
            onPress={() => setCurrentPage((value) => Math.min(totalPages, value + 1))}
            disabled={currentPage >= totalPages || loading}
          />
          <Button
            size="$3"
            chromeless
            aria-label="Last page"
            icon={LastPageIcon ? <LastPageIcon size="sm" color="$textSecondary" /> : undefined}
            onPress={() => setCurrentPage(totalPages)}
            disabled={currentPage >= totalPages || loading}
          />
        </XStack>
      </XStack>
    </YStack>
  );
}

export default DirView;
