import React, { useCallback, useEffect, useState } from 'react';
import { Box, Button, Icon, IconButton, Icons, Scroll, Spinner, Text, color, config } from 'folds';
import { MatrixEvent } from 'matrix-js-sdk';
import { Page, PageContent, PageHeader } from '../../components/page';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { useRoom } from '../../hooks/useRoom';

// ── Types ─────────────────────────────────────────────────────────────────────

type ActivityFilter = 'all' | 'members' | 'power' | 'room';

const STATE_EVENT_TYPES = [
  'm.room.member',
  'm.room.power_levels',
  'm.room.name',
  'm.room.topic',
  'm.room.avatar',
  'm.room.server_acl',
] as const;

type StateEventType = (typeof STATE_EVENT_TYPES)[number];

// ── Timestamp formatting ──────────────────────────────────────────────────────

function formatRelativeTs(ts: number): string {
  const diff = Date.now() - ts;
  if (diff < 60000) return 'just now';
  if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;
  if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;
  const d = new Date(ts);
  const sameYear = d.getFullYear() === new Date().getFullYear();
  return d.toLocaleDateString('en-US', {
    month: 'short',
    day: 'numeric',
    ...(sameYear ? {} : { year: 'numeric' }),
  });
}

// ── Event description ─────────────────────────────────────────────────────────

function getDisplayName(mx: ReturnType<typeof useMatrixClient>, userId: string): string {
  return mx.getUser(userId)?.displayName ?? userId;
}

type EventDesc = {
  text: React.ReactNode;
  iconSrc: (typeof Icons)[keyof typeof Icons];
  filter: ActivityFilter;
};

function describeEvent(mx: ReturnType<typeof useMatrixClient>, ev: MatrixEvent): EventDesc | null {
  const type = ev.getType() as StateEventType;
  const sender = ev.getSender() ?? '';
  const content = ev.getContent<Record<string, unknown>>();
  const prevContent = ev.getPrevContent() as Record<string, unknown>;

  const senderName = getDisplayName(mx, sender);
  const stateKey = ev.getStateKey() ?? '';

  switch (type) {
    case 'm.room.member': {
      const membership = content.membership as string | undefined;
      const prevMembership = prevContent.membership as string | undefined;
      const reason = content.reason as string | undefined;
      const targetName = getDisplayName(mx, stateKey);

      if (membership === 'join') {
        if (
          prevMembership === 'invite' ||
          prevMembership === 'knock' ||
          prevMembership === undefined ||
          prevMembership === null
        ) {
          return {
            text: (
              <>
                <strong>{targetName}</strong> joined
              </>
            ),
            iconSrc: Icons.User,
            filter: 'members',
          };
        }
        // prevMembership === 'join' → profile update
        return {
          text: (
            <>
              <strong>{targetName}</strong> updated their profile
            </>
          ),
          iconSrc: Icons.User,
          filter: 'members',
        };
      }

      if (membership === 'leave') {
        if (prevMembership === 'ban') {
          return {
            text: (
              <>
                <strong>{targetName}</strong> was unbanned by <strong>{senderName}</strong>
              </>
            ),
            iconSrc: Icons.User,
            filter: 'members',
          };
        }
        if (sender === stateKey) {
          return {
            text: (
              <>
                <strong>{targetName}</strong> left
              </>
            ),
            iconSrc: Icons.User,
            filter: 'members',
          };
        }
        // sender !== stateKey and the target was only invited → the inviter (or a
        // moderator) retracted the invite; this is not a kick.
        if (prevMembership === 'invite') {
          return {
            text: (
              <>
                <strong>{senderName}</strong> withdrew the invite to <strong>{targetName}</strong>
              </>
            ),
            iconSrc: Icons.User,
            filter: 'members',
          };
        }
        return {
          text: (
            <>
              <strong>{targetName}</strong> was kicked by <strong>{senderName}</strong>
              {reason ? ` (${reason})` : ''}
            </>
          ),
          iconSrc: Icons.User,
          filter: 'members',
        };
      }

      if (membership === 'ban') {
        return {
          text: (
            <>
              <strong>{targetName}</strong> was banned by <strong>{senderName}</strong>
              {reason ? ` (${reason})` : ''}
            </>
          ),
          iconSrc: Icons.User,
          filter: 'members',
        };
      }

      if (membership === 'invite') {
        return {
          text: (
            <>
              <strong>{targetName}</strong> was invited by <strong>{senderName}</strong>
            </>
          ),
          iconSrc: Icons.UserPlus,
          filter: 'members',
        };
      }

      return null;
    }

    case 'm.room.power_levels':
      return {
        text: (
          <>
            Power levels updated by <strong>{senderName}</strong>
          </>
        ),
        iconSrc: Icons.ShieldUser,
        filter: 'power',
      };

    case 'm.room.name': {
      const newName = content.name as string | undefined;
      return {
        text: (
          <>
            Room renamed to &ldquo;<strong>{newName ?? ''}</strong>&rdquo; by{' '}
            <strong>{senderName}</strong>
          </>
        ),
        iconSrc: Icons.Pencil,
        filter: 'room',
      };
    }

    case 'm.room.topic':
      return {
        text: (
          <>
            Topic updated by <strong>{senderName}</strong>
          </>
        ),
        iconSrc: Icons.Pencil,
        filter: 'room',
      };

    case 'm.room.avatar':
      return {
        text: (
          <>
            Room avatar changed by <strong>{senderName}</strong>
          </>
        ),
        iconSrc: Icons.Photo,
        filter: 'room',
      };

    case 'm.room.server_acl':
      return {
        text: (
          <>
            Server ACL updated by <strong>{senderName}</strong>
          </>
        ),
        iconSrc: Icons.Shield,
        filter: 'room',
      };

    default:
      return null;
  }
}

// ── Filter chip ───────────────────────────────────────────────────────────────

type FilterChipProps = {
  label: string;
  active: boolean;
  onClick: () => void;
};

function FilterChip({ label, active, onClick }: FilterChipProps) {
  return (
    <Button
      size="300"
      variant={active ? 'Primary' : 'Secondary'}
      fill={active ? 'Soft' : 'None'}
      radii="300"
      onClick={onClick}
    >
      <Text size="B300">{label}</Text>
    </Button>
  );
}

// ── Log entry ─────────────────────────────────────────────────────────────────

type LogEntryProps = {
  ev: MatrixEvent;
  desc: EventDesc;
};

function LogEntry({ ev, desc }: LogEntryProps) {
  return (
    <Box
      alignItems="Center"
      gap="300"
      style={{
        padding: `${config.space.S200} ${config.space.S300}`,
        borderRadius: config.radii.R300,
        border: `1px solid ${color.Surface.ContainerLine}`,
        background: color.Surface.Container,
      }}
    >
      <Box
        shrink="No"
        alignItems="Center"
        justifyContent="Center"
        style={{
          width: 32,
          height: 32,
          borderRadius: config.radii.R300,
          background: color.SurfaceVariant.Container,
          flexShrink: 0,
        }}
      >
        <Icon src={desc.iconSrc} size="200" />
      </Box>
      <Box grow="Yes" direction="Column" style={{ overflow: 'hidden', gap: 2 }}>
        <Text size="T300" style={{ lineHeight: 1.4 }}>
          {desc.text}
        </Text>
        <Text size="T200" priority="300">
          {formatRelativeTs(ev.getTs())}
        </Text>
      </Box>
    </Box>
  );
}

// ── Main component ────────────────────────────────────────────────────────────

type RoomActivityLogProps = {
  requestClose: () => void;
};

export function RoomActivityLog({ requestClose }: RoomActivityLogProps) {
  const mx = useMatrixClient();
  const room = useRoom();

  const [filter, setFilter] = useState<ActivityFilter>('all');
  const [loading, setLoading] = useState(false);
  const [hasLoadedOnce, setHasLoadedOnce] = useState(false);
  const [canLoadMore, setCanLoadMore] = useState(true);

  const getStateEvents = useCallback((): MatrixEvent[] => {
    const typeSet = new Set<string>(STATE_EVENT_TYPES);
    return room
      .getLiveTimeline()
      .getEvents()
      .filter((ev) => typeSet.has(ev.getType()) && !ev.isRedacted())
      .slice()
      .reverse();
  }, [room]);

  const [events, setEvents] = useState<MatrixEvent[]>(() => getStateEvents());

  useEffect(() => {
    setEvents(getStateEvents());
  }, [getStateEvents]);

  const handleLoadMore = useCallback(async () => {
    if (loading || !canLoadMore) return;
    setLoading(true);
    try {
      const hasMore = await mx.paginateEventTimeline(room.getLiveTimeline(), {
        backwards: true,
        limit: 50,
      });
      setEvents(getStateEvents());
      setCanLoadMore(hasMore);
      setHasLoadedOnce(true);
    } catch {
      // silently fail — user can retry
    } finally {
      setLoading(false);
    }
  }, [loading, canLoadMore, mx, room, getStateEvents]);

  // Auto-paginate on mount — state events are rarely in the initial sync
  // window, so we immediately fetch backwards to populate the log.
  useEffect(() => {
    handleLoadMore();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Build described entries
  const entries: Array<{ ev: MatrixEvent; desc: EventDesc }> = [];
  for (const ev of events) {
    const desc = describeEvent(mx, ev);
    if (!desc) continue;
    if (filter !== 'all' && desc.filter !== filter) continue;
    entries.push({ ev, desc });
  }

  return (
    <Page>
      <PageHeader outlined={false}>
        <Box grow="Yes" gap="200">
          <Box grow="Yes" alignItems="Center" gap="200">
            <Text as="h2" size="H3" truncate>
              Activity Log
            </Text>
          </Box>
          <Box shrink="No">
            <IconButton onClick={requestClose} variant="Surface" aria-label="Close">
              <Icon src={Icons.Cross} />
            </IconButton>
          </Box>
        </Box>
      </PageHeader>

      {/* Filter chips */}
      <Box
        shrink="No"
        gap="100"
        wrap="Wrap"
        style={{ padding: `${config.space.S200} ${config.space.S300}` }}
      >
        {(
          [
            { key: 'all', label: 'All' },
            { key: 'members', label: 'Members' },
            { key: 'power', label: 'Power' },
            { key: 'room', label: 'Room changes' },
          ] as { key: ActivityFilter; label: string }[]
        ).map(({ key, label }) => (
          <FilterChip
            key={key}
            label={label}
            active={filter === key}
            onClick={() => setFilter(key)}
          />
        ))}
      </Box>

      <Box grow="Yes">
        <Scroll hideTrack visibility="Hover">
          <PageContent>
            <Box direction="Column" gap="200">
              {/* Loading skeleton on first load */}
              {loading && !hasLoadedOnce && (
                <Box justifyContent="Center" style={{ padding: config.space.S400 }}>
                  <Spinner />
                </Box>
              )}

              {/* Empty state */}
              {!loading && entries.length === 0 && (
                <Box
                  direction="Column"
                  alignItems="Center"
                  gap="200"
                  style={{ padding: config.space.S600 }}
                >
                  <Icon src={Icons.RecentClock} size="600" />
                  <Text size="T300" priority="300" align="Center">
                    No room activity found
                  </Text>
                </Box>
              )}

              {/* Log entries */}
              {entries.map(({ ev, desc }) => (
                <LogEntry key={ev.getId()} ev={ev} desc={desc} />
              ))}

              {/* Load more */}
              {canLoadMore && !loading && (
                <Box justifyContent="Center" style={{ paddingTop: config.space.S200 }}>
                  <Button
                    size="300"
                    variant="Secondary"
                    fill="Soft"
                    radii="300"
                    onClick={handleLoadMore}
                  >
                    <Text size="B300">Load more</Text>
                  </Button>
                </Box>
              )}

              {/* Inline spinner while paginating */}
              {loading && hasLoadedOnce && (
                <Box justifyContent="Center" style={{ padding: config.space.S300 }}>
                  <Spinner />
                </Box>
              )}

              {/* End of history */}
              {!canLoadMore && hasLoadedOnce && (
                <Text
                  size="T200"
                  priority="300"
                  align="Center"
                  style={{ padding: `${config.space.S200} 0` }}
                >
                  Beginning of history
                </Text>
              )}
            </Box>
          </PageContent>
        </Scroll>
      </Box>
    </Page>
  );
}
