import React, { useMemo } from 'react';
import { Tabs, VirtualizedTable, Group, Card, Box, Divider, ScrollArea, Typography, Progress, Stack } from "tidbcloud-uikit/components";
import { Prism } from 'tidbcloud-uikit/prism';
import { format } from 'sql-formatter';

import { withModelContext } from './withModelContext';
import { useModelState } from './model';

interface SQLDataViewerState {
  table: {
    columns: { header: string; accessor: string }[];
    data: any[];
  };
  sql: string;
  stats: ColumnStat[]
}

interface ColumnStat {
  name: string;
  overall: {
    type: "numerical" | "textual";
    count: number;
    distinct: number;
    missing: number;
  };
  distribution: {
    unique?: number;
    enum?: { name: string; count: number }[];
    numerical?: Array<[string, string | number]>;
  };
}

function Table({ columns, data }: SQLDataViewerState["table"]) {
  return (
    <VirtualizedTable
      columns={columns.map((i: any) => ({
        ...i,
        Header: i.header ?? i.Header,
      }))}
      data={data}
      height={450}
      itemSize={() => {
        return 40;
      }}
    />
  );
}

function SQLCode({ sql }: { sql: string }) {
  return (
    <Prism
      language="sql"
      styles={(theme) => ({
        code: {
          backgroundColor: "transparent !important",
          wordBreak: "break-all",
        },
        line: {
          padding: "0 0 0 16px",
          whiteSpace: "pre",
          color: theme.colors.gray[9],
          width: "unset",
        },
        lineContent: {
          whiteSpace: "pre-wrap",
        },
      })}>
      {sql}
    </Prism>
  );
}

function StatItem({ name, value }: { name: string; value: string|number}) {
  return (
    <Group position="apart">
      <Typography variant="label-md">{name}</Typography>
      <Typography variant="body-md" >{value}</Typography>
    </Group>
  )
}

function Stats({ stats }: Pick<SQLDataViewerState, "stats">) {
  return (
    <ScrollArea>
      <Group spacing={0} noWrap align="flex-start">
        {stats.map((i) => (
          <Card key={i.name} withBorder w={220} h={500} p={0} radius={0}>
            <Box component="header" p={12}>
              <Typography variant="label-lg">{i.name}</Typography>
            </Box>
            <Divider />
            <Box p={12}>
              <StatItem name="Data type" value={i.overall.type}/>
              <StatItem name="Count" value={i.overall.count}/>
              <StatItem name="Distinct" value={i.overall.distinct}/>
              <StatItem name="Missing" value={i.overall.missing}/>
            </Box>
            <Divider />
            {i.distribution.unique && (
              <Stack align="center" spacing={0} mt={24}>
                <Typography color="cyan" fw={700} size={32} lh="32px">{i.distribution.unique}</Typography>
                <Typography variant="body-md">unique values</Typography>
              </Stack>
            )}
            {i.distribution.enum && (
              <Stack p={8} >
                {i.distribution.enum.map(k => (
                  <Stack key={k.name} spacing={0}>
                    <Group position="apart" noWrap>
                      <Typography variant="label-md">{k.name}</Typography>
                      <Typography variant="body-md">{k.count}</Typography>
                    </Group>
                    <Progress value={k.count / i.overall.count * 100}/>
                  </Stack>
                ))}
              </Stack>
            )}
            {i.distribution.numerical && (
              <Stack p={8} spacing={4}>
                {i.distribution.numerical.map(([key, val]) => (
                  <Stack key={key} spacing={0}>
                    <Group position="apart" noWrap>
                      <Typography variant="label-md">{key}</Typography>
                      <Typography variant="body-md">{val}</Typography>
                    </Group>
                  </Stack>
                ))}
              </Stack>
            )}
          </Card>
        ))}
      </Group>
    </ScrollArea>
  );
}

function SQLDataViewer() {
  const [table] = useModelState<SQLDataViewerState, 'table'>('table');
  const [sql] = useModelState<SQLDataViewerState, 'sql'>('sql');
  const [stats] = useModelState<SQLDataViewerState, 'stats'>('stats');

  const hasTable = !!table && !!table.columns && !!table.data
  const hasSql = !!sql
  const hasStats = !!stats && stats.length > 0

  const sqlCode = useMemo(() => {
    if (!sql) return ''

    try {
      return format(sql ?? '');
    } catch {
      return sql ?? '';
    }
  }, [sql]);

  return (
    <Tabs defaultValue="table">
      <Tabs.List>
        {hasTable && <Tabs.Tab value="table">Table</Tabs.Tab>}
        {hasSql && <Tabs.Tab value="sql">SQL</Tabs.Tab>}
        {hasStats && <Tabs.Tab value="stat">Statistic</Tabs.Tab>}
      </Tabs.List>

      {hasTable && <Tabs.Panel value="table" pt="xs">
        <Table columns={table.columns} data={table.data} />
      </Tabs.Panel>}

      {hasSql && <Tabs.Panel value="sql" pt="xs">
        <SQLCode sql={sqlCode} />
      </Tabs.Panel>}

      {hasStats && <Tabs.Panel value="stat" pt="xs">
        <Stats stats={stats} />
      </Tabs.Panel>}
    </Tabs>
  );
}

export default withModelContext(SQLDataViewer);
