import { useEffect, useMemo, useRef, useState } from 'react';
import { CodeBlock } from '@/components/ui/CodeBlock';
import {
  IconCheckCircle,
  IconCodeWrap,
  IconCrossCircle,
  IconLandingAI,
  IconListUnordered,
  IconTerminalWindow,
  IconUser,
  IconGlowingDot,
} from '@/components/ui/Icons';
import { MessageUI } from '@/lib/types';
import { ChunkBody, CodeResult, formatStreamLogs } from '@/lib/utils/content';
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from '../ui/Table';
import { Button } from '../ui/Button';
import { Dialog, DialogContent, DialogTrigger } from '../ui/Dialog';
import Img from '../ui/Img';
import CodeResultDisplay from '../CodeResultDisplay';
import { useAtom, useSetAtom } from 'jotai';
import { selectedMessageId } from '@/state/chat';
import { Message } from '@prisma/client';
import { Separator } from '../ui/Separator';
import { cn } from '@/lib/utils';
import { usePrevious } from '@/lib/hooks/usePrevious';

export interface ChatMessageProps {
  message: Message;
  wipAssistantMessage?: MessageUI;
}

export const ChatMessage: React.FC<ChatMessageProps> = ({
  message,
  wipAssistantMessage,
}) => {
  const [messageId, setMessageId] = useAtom(selectedMessageId);
  const { id, mediaUrl, prompt, response, result } = message;
  const [formattedSections, codeResult] = useMemo(
    () => formatStreamLogs(response ?? wipAssistantMessage?.content),
    [response, wipAssistantMessage?.content],
  );
  return (
    <div
      className={cn(
        'rounded-md bg-muted border border-muted p-4 mb-4',
        messageId === id && 'lg:border-primary/50',
        result && 'lg:cursor-pointer',
      )}
      onClick={() => {
        if (result) {
          setMessageId(id);
        }
      }}
    >
      <div className="flex">
        <div className="flex size-8 shrink-0 select-none items-center justify-center rounded-md border shadow bg-background">
          <IconUser />
        </div>
        <div className="flex-1 px-1 ml-4 space-y-2 overflow-hidden">
          <p>{prompt}</p>
          {mediaUrl && (
            <>
              {mediaUrl?.endsWith('.mp4') ? (
                <video src={mediaUrl} controls width={400} height={400} />
              ) : (
                <Dialog>
                  <DialogTrigger asChild>
                    <Img src={mediaUrl} alt={mediaUrl} width={300} />
                  </DialogTrigger>
                  <DialogContent className="max-w-5xl">
                    <Img src={mediaUrl} alt={mediaUrl} quality={100} />
                  </DialogContent>
                </Dialog>
              )}
            </>
          )}
        </div>
      </div>
      {!!formattedSections.length && (
        <>
          <Separator className="bg-primary/30 my-4" />
          <div className="flex">
            <div className="flex size-8 shrink-0 select-none items-center justify-center rounded-md border shadow bg-primary text-primary-foreground">
              <IconLandingAI />
            </div>
            <div className="flex-1 px-1 space-y-4 ml-4 overflow-hidden">
              <Table className="w-[400px]">
                <TableBody>
                  {formattedSections.map(section => (
                    <TableRow
                      className="border-primary/50 h-[56px]"
                      key={section.type}
                    >
                      <TableCell className="text-center text-webkit-center">
                        {ChunkStatusToIconDict[section.status]}
                      </TableCell>
                      <TableCell className="font-medium">
                        <ChunkTypeToText chunk={section} />
                      </TableCell>
                      <TableCell className="text-right">
                        <ChunkPayloadAction payload={section.payload} />
                      </TableCell>
                    </TableRow>
                  ))}
                </TableBody>
              </Table>
              {codeResult && (
                <div className="xl:hidden">
                  <CodeResultDisplay codeResult={codeResult} />
                </div>
              )}
              {codeResult && <p>✨ Coding complete</p>}
            </div>
          </div>
        </>
      )}
    </div>
  );
};

const ChunkStatusToIconDict: Record<ChunkBody['status'], React.ReactElement> = {
  started: <IconGlowingDot className="bg-yellow-500/80" />,
  completed: <IconCheckCircle className="text-green-500" />,
  running: <IconGlowingDot className="bg-teal-500/80" />,
  failed: <IconCrossCircle className="text-red-500" />,
};
const ChunkTypeToText: React.FC<{
  chunk: ChunkBody;
}> = ({ chunk }) => {
  const { status, type } = chunk;

  const [seconds, setSeconds] = useState(0);
  const isExecution = type === 'code' && status === 'running';
  const timerId = useRef<NodeJS.Timeout>();

  useEffect(() => {
    if (isExecution) {
      const timerId = setInterval(() => {
        setSeconds(prevSeconds => Math.round((prevSeconds + 0.2) * 10) / 10);
      }, 200);
      return () => clearInterval(timerId);
    }
  }, [isExecution]);

  if (type === 'plans') return <p>Creating instructions</p>;
  if (type === 'tools') return <p>Retrieving tools</p>;
  if (type === 'code' && status === 'started') return <p>Generating code</p>;
  if (isExecution) return <p>Executing code ({seconds}s)</p>;
  if (type === 'code' && status === 'completed')
    return <p>Code execution success ({seconds}s)</p>;
  if (type === 'code' && status === 'failed')
    return <p>Code execution failure ({seconds}s)</p>;

  return null;
};
const ChunkPayloadAction: React.FC<{
  payload: ChunkBody['payload'];
}> = ({ payload }) => {
  if (!payload) return null;
  if (Array.isArray(payload)) {
    // [{title: 123, content, 345}, {title: ..., content: ...}] => ['title', 'content']
    const keyArray = Array.from(
      payload.reduce((acc, curr) => {
        Object.keys(curr).forEach(key => acc.add(key));
        return acc;
      }, new Set<string>()),
    );

    return (
      <Dialog>
        <DialogTrigger asChild>
          <Button variant="ghost" size="icon">
            <IconListUnordered />
          </Button>
        </DialogTrigger>
        <DialogContent
          className="max-w-5xl"
          onOpenAutoFocus={e => e.preventDefault()}
        >
          <Table>
            <TableHeader>
              <TableRow className="border-primary/50">
                {keyArray.map(header => (
                  <TableHead key={header}>{header}</TableHead>
                ))}
              </TableRow>
            </TableHeader>
            <TableBody>
              {payload.map((line, index) => (
                <TableRow className="border-primary/50" key={index}>
                  {keyArray.map(header =>
                    header === 'documentation' ? (
                      <TableCell key={header}>
                        <Dialog>
                          <DialogTrigger asChild>
                            <Button
                              variant="ghost"
                              size="icon"
                              className="size-8 ml-[40%]"
                            >
                              <IconTerminalWindow className="text-teal-500 size-4" />
                            </Button>
                          </DialogTrigger>
                          <DialogContent className="max-w-5xl">
                            <CodeBlock language="md" value={line[header]} />
                          </DialogContent>
                        </Dialog>
                      </TableCell>
                    ) : (
                      <TableCell key={header}>{line[header]}</TableCell>
                    ),
                  )}
                </TableRow>
              ))}
            </TableBody>
          </Table>
        </DialogContent>
      </Dialog>
    );
  } else {
    return (
      <Dialog>
        <DialogTrigger asChild>
          <Button variant="ghost" size="icon">
            <IconCodeWrap />
          </Button>
        </DialogTrigger>
        <DialogContent className="max-w-5xl">
          <CodeResultDisplay codeResult={payload as CodeResult} />
        </DialogContent>
      </Dialog>
    );
  }
};

export default ChatMessage;
