import React, { useCallback, useEffect, useState } from 'react';
import { Box, Text, Switch, Button, Chip, Icon, Icons, color, config, Spinner } from 'folds';
import { IPusherRequest } from 'matrix-js-sdk';
import { useAtomValue, useSetAtom } from 'jotai';
import { NOTIFICATION_SOUND_MAP } from '../../../utils/notificationSounds';
import { SequenceCard } from '../../../components/sequence-card';
import { SequenceCardStyle } from '../styles.css';
import { SettingTile } from '../../../components/setting-tile';
import { useSetting } from '../../../state/hooks/settings';
import { settingsAtom } from '../../../state/settings';
import {
  isSnoozeActive,
  nextTimeAtHour,
  notificationSnoozeUntilAtom,
  SNOOZE_INDEFINITE,
} from '../../../state/notificationSnooze';
import { formatFriendlyDateTime } from '../../../utils/datetimeInput';
import { getNotificationState, usePermissionState } from '../../../hooks/usePermission';
import { useEmailNotifications } from '../../../hooks/useEmailNotifications';
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
import { useMatrixClient } from '../../../hooks/useMatrixClient';

function EmailNotification() {
  const mx = useMatrixClient();
  const [result, refreshResult] = useEmailNotifications();

  const [setState, setEnable] = useAsyncCallback(
    useCallback(
      async (email: string, enable: boolean) => {
        if (enable) {
          await mx.setPusher({
            kind: 'email',
            app_id: 'm.email',
            pushkey: email,
            app_display_name: 'Email Notifications',
            device_display_name: email,
            lang: 'en',
            data: {
              brand: 'Lotus Chat',
            },
            append: true,
          });
          return;
        }
        await mx.setPusher({
          pushkey: email,
          app_id: 'm.email',
          kind: null,
        } as unknown as IPusherRequest);
      },
      [mx],
    ),
  );

  const handleChange = (value: boolean) => {
    if (result && result.email) {
      setEnable(result.email, value).then(() => {
        refreshResult();
      });
    }
  };

  return (
    <SettingTile
      title="Email Notification"
      description={
        <>
          {result && !result.email && (
            <Text as="span" style={{ color: color.Critical.Main }} size="T200">
              Your account does not have any email attached.
            </Text>
          )}
          {result && result.email && <>Send notification to your email. {`("${result.email}")`}</>}
          {result === null && (
            <Text as="span" style={{ color: color.Critical.Main }} size="T200">
              Unexpected Error!
            </Text>
          )}
          {result === undefined && 'Send notification to your email.'}
        </>
      }
      after={
        <>
          {setState.status !== AsyncStatus.Loading &&
            typeof result === 'object' &&
            result?.email && <Switch value={result.enabled} onChange={handleChange} />}
          {(setState.status === AsyncStatus.Loading || result === undefined) && (
            <Spinner variant="Secondary" />
          )}
        </>
      }
    />
  );
}

const SOUND_OPTIONS: { label: string; value: 'notification' | 'invite' | 'call' | 'none' }[] = [
  { label: 'Default', value: 'notification' },
  { label: 'Ping', value: 'invite' },
  { label: 'Call', value: 'call' },
  { label: 'None', value: 'none' },
];

const INVITE_SOUND_OPTIONS: {
  label: string;
  value: 'notification' | 'invite' | 'call' | 'none';
}[] = [
  { label: 'Default', value: 'invite' },
  { label: 'Ping', value: 'notification' },
  { label: 'Call', value: 'call' },
  { label: 'None', value: 'none' },
];

function playPreview(soundId: string) {
  const src = NOTIFICATION_SOUND_MAP[soundId];
  if (!src) return;
  new Audio(src).play().catch(() => undefined);
}

const selectStyle: React.CSSProperties = {
  background: color.SurfaceVariant.Container,
  border: `1px solid ${color.SurfaceVariant.ContainerLine}`,
  borderRadius: config.radii.R300,
  color: color.SurfaceVariant.OnContainer,
  colorScheme: 'dark',
  fontSize: '0.82rem',
  padding: `${config.space.S100} ${config.space.S200}`,
  cursor: 'pointer',
  outline: 'none',
};

const SNOOZE_PRESETS: Array<{ label: string; resolve: (now: number) => number }> = [
  { label: '30 minutes', resolve: (now) => now + 30 * 60_000 },
  { label: '1 hour', resolve: (now) => now + 60 * 60_000 },
  { label: '4 hours', resolve: (now) => now + 4 * 60 * 60_000 },
  { label: 'Until 8 AM', resolve: (now) => nextTimeAtHour(8, now) },
  { label: 'Until I resume', resolve: () => SNOOZE_INDEFINITE },
];

// Cross-platform "pause notifications" — sets a snooze instant that the
// notification gate (ClientNonUIFeatures) reads to suppress alerts + sounds.
function PauseNotifications() {
  const snoozeUntil = useAtomValue(notificationSnoozeUntilAtom);
  const setSnoozeUntil = useSetAtom(notificationSnoozeUntilAtom);
  // While paused, tick so the status flips to "on" the moment the snooze lapses.
  const [, setTick] = useState(0);
  const active = isSnoozeActive(snoozeUntil);

  useEffect(() => {
    if (!active) return undefined;
    const id = setInterval(() => setTick((n) => n + 1), 30_000);
    return () => clearInterval(id);
  }, [active]);

  const status = !active
    ? 'Notifications are on.'
    : snoozeUntil >= SNOOZE_INDEFINITE
      ? 'Paused until you resume.'
      : `Paused until ${formatFriendlyDateTime(snoozeUntil)}.`;

  return (
    <SettingTile
      title="Pause Notifications"
      description={<span style={active ? { color: color.Warning.Main } : undefined}>{status}</span>}
      after={
        active ? (
          <Button
            size="300"
            variant="Secondary"
            fill="Soft"
            radii="300"
            onClick={() => setSnoozeUntil(0)}
            before={<Icon size="100" src={Icons.BellRing} />}
          >
            <Text size="B300">Resume</Text>
          </Button>
        ) : undefined
      }
    >
      <Box gap="200" wrap="Wrap" style={{ marginTop: config.space.S200 }}>
        {SNOOZE_PRESETS.map((preset) => (
          <Chip
            key={preset.label}
            variant="SurfaceVariant"
            radii="Pill"
            onClick={() => setSnoozeUntil(preset.resolve(Date.now()))}
          >
            <Text size="B300">{preset.label}</Text>
          </Chip>
        ))}
      </Box>
    </SettingTile>
  );
}

export function SystemNotification() {
  const notifPermission = usePermissionState('notifications', getNotificationState());
  const [showNotifications, setShowNotifications] = useSetting(settingsAtom, 'showNotifications');
  const [isNotificationSounds, setIsNotificationSounds] = useSetting(
    settingsAtom,
    'isNotificationSounds',
  );
  const [messageSoundId, setMessageSoundId] = useSetting(settingsAtom, 'messageSoundId');
  const [inviteSoundId, setInviteSoundId] = useSetting(settingsAtom, 'inviteSoundId');
  const [quietHoursEnabled, setQuietHoursEnabled] = useSetting(settingsAtom, 'quietHoursEnabled');
  const [quietHoursStart, setQuietHoursStart] = useSetting(settingsAtom, 'quietHoursStart');
  const [quietHoursEnd, setQuietHoursEnd] = useSetting(settingsAtom, 'quietHoursEnd');

  const requestNotificationPermission = () => {
    window.Notification.requestPermission();
  };

  return (
    <Box direction="Column" gap="100">
      <Text size="L400">System</Text>
      <SequenceCard
        className={SequenceCardStyle}
        variant="SurfaceVariant"
        direction="Column"
        gap="400"
      >
        <SettingTile
          title="Desktop Notifications"
          description={
            notifPermission === 'denied' ? (
              <Text as="span" style={{ color: color.Critical.Main }} size="T200">
                {'Notification' in window
                  ? 'Notification permission is blocked. Please allow notification permission from browser address bar.'
                  : 'Notifications are not supported by the system.'}
              </Text>
            ) : (
              <span>Show desktop notifications when message arrive.</span>
            )
          }
          after={
            notifPermission === 'prompt' ? (
              <Button size="300" radii="300" onClick={requestNotificationPermission}>
                <Text size="B300">Enable</Text>
              </Button>
            ) : (
              <Switch
                disabled={notifPermission !== 'granted'}
                value={showNotifications}
                onChange={setShowNotifications}
              />
            )
          }
        />
      </SequenceCard>
      <SequenceCard
        className={SequenceCardStyle}
        variant="SurfaceVariant"
        direction="Column"
        gap="400"
      >
        <PauseNotifications />
      </SequenceCard>
      <SequenceCard
        className={SequenceCardStyle}
        variant="SurfaceVariant"
        direction="Column"
        gap="400"
      >
        <SettingTile
          title="Notification Sound"
          description="Play sound when new message arrive."
          after={<Switch value={isNotificationSounds} onChange={setIsNotificationSounds} />}
        />
        {isNotificationSounds && (
          <Box direction="Column" gap="300" style={{ paddingTop: config.space.S100 }}>
            <Box alignItems="Center" gap="200">
              <Text size="T300" style={{ flexGrow: 1, color: color.SurfaceVariant.OnContainer }}>
                Message Sound
              </Text>
              <select
                value={messageSoundId}
                onChange={(e) =>
                  setMessageSoundId(e.target.value as 'notification' | 'invite' | 'call' | 'none')
                }
                aria-label="Message notification sound"
                style={selectStyle}
              >
                {SOUND_OPTIONS.map((opt) => (
                  <option
                    key={opt.value}
                    value={opt.value}
                    style={{
                      background: color.SurfaceVariant.Container,
                      color: color.SurfaceVariant.OnContainer,
                    }}
                  >
                    {opt.label}
                  </option>
                ))}
              </select>
              {messageSoundId !== 'none' && (
                <Button
                  size="300"
                  radii="300"
                  variant="Secondary"
                  onClick={() => playPreview(messageSoundId)}
                  aria-label="Preview message sound"
                >
                  <Text size="B300">▶</Text>
                </Button>
              )}
            </Box>
            <Box alignItems="Center" gap="200">
              <Text size="T300" style={{ flexGrow: 1, color: color.SurfaceVariant.OnContainer }}>
                Invite Sound
              </Text>
              <select
                value={inviteSoundId}
                onChange={(e) =>
                  setInviteSoundId(e.target.value as 'notification' | 'invite' | 'call' | 'none')
                }
                aria-label="Invite notification sound"
                style={selectStyle}
              >
                {INVITE_SOUND_OPTIONS.map((opt) => (
                  <option
                    key={opt.value}
                    value={opt.value}
                    style={{
                      background: color.SurfaceVariant.Container,
                      color: color.SurfaceVariant.OnContainer,
                    }}
                  >
                    {opt.label}
                  </option>
                ))}
              </select>
              {inviteSoundId !== 'none' && (
                <Button
                  size="300"
                  radii="300"
                  variant="Secondary"
                  onClick={() => playPreview(inviteSoundId)}
                  aria-label="Preview invite sound"
                >
                  <Text size="B300">▶</Text>
                </Button>
              )}
            </Box>
          </Box>
        )}
      </SequenceCard>
      <SequenceCard
        className={SequenceCardStyle}
        variant="SurfaceVariant"
        direction="Column"
        gap="400"
      >
        <SettingTile
          title="Quiet Hours"
          description="Notifications are silenced between these times. Handles overnight (e.g. 11:00 PM – 8:00 AM)."
          after={<Switch value={quietHoursEnabled} onChange={setQuietHoursEnabled} />}
        />
        {quietHoursEnabled && (
          <Box direction="Column" gap="300" style={{ paddingTop: config.space.S100 }}>
            <Box alignItems="Center" gap="200">
              <Text size="T300" style={{ flexGrow: 1, color: color.SurfaceVariant.OnContainer }}>
                Start
              </Text>
              <input
                type="time"
                value={quietHoursStart}
                onChange={(e) => setQuietHoursStart(e.target.value)}
                aria-label="Quiet hours start time"
                style={selectStyle}
              />
            </Box>
            <Box alignItems="Center" gap="200">
              <Text size="T300" style={{ flexGrow: 1, color: color.SurfaceVariant.OnContainer }}>
                End
              </Text>
              <input
                type="time"
                value={quietHoursEnd}
                onChange={(e) => setQuietHoursEnd(e.target.value)}
                aria-label="Quiet hours end time"
                style={selectStyle}
              />
            </Box>
            {(!quietHoursStart || !quietHoursEnd) && (
              <Text size="T200" style={{ color: color.Critical.Main }}>
                Set both a start and end time — quiet hours stay inactive until both are filled in.
              </Text>
            )}
          </Box>
        )}
      </SequenceCard>
      <SequenceCard
        className={SequenceCardStyle}
        variant="SurfaceVariant"
        direction="Column"
        gap="400"
      >
        <EmailNotification />
      </SequenceCard>
    </Box>
  );
}
