import { useEffect, useMemo, useState, type ReactNode } from "react";
import { Alert, Platform, Pressable, RefreshControl, ScrollView, StyleSheet, Text, View } from "react-native";
import { router } from "expo-router";

import { GlassSurface } from "@/components/GlassSurface";
import { LoadingScreen } from "@/components/LoadingScreen";
import { Button } from "@/components/ui/Button";
import { Card } from "@/components/ui/Card";
import { DateTimeField } from "@/components/ui/DateTimeField";
import { Input } from "@/components/ui/Input";
import { SelectField } from "@/components/ui/SelectField";
import { fonts, spacing } from "@/constants/theme";
import { useAppTheme } from "@/contexts/ThemeContext";
import { useTabBarScrollPadding } from "@/lib/tab-bar-insets";
import { tabLayout } from "@/lib/tab-layout";
import { formatDateTime } from "@/lib/format";
import type { ThemeColors } from "@/lib/theme-palette";
import { useThemedStyles } from "@/lib/use-themed-styles";
import {
  endTimeClockLiveActivity,
  syncTimeClockLiveActivity,
} from "@/lib/time-clock-live-activity";
import { describeClockOutOutcome, formatElapsedSeconds } from "@/lib/time-clock";
import { useRunningElapsed } from "@/lib/use-running-elapsed";
import { api } from "@/lib/trpc";

export type TimeClockPanelProps = {
  defaultClientId?: string;
  defaultInvoiceId?: string;
  /** Hides the in-panel title card when idle (tab screen already has PageHeader). */
  compact?: boolean;
  header?: ReactNode;
};

export function TimeClockPanel({
  defaultClientId = "",
  defaultInvoiceId = "",
  compact = false,
  header,
}: TimeClockPanelProps) {
  const { colors } = useAppTheme();
  const styles = useThemedStyles(createTimeClockStyles);
  const utils = api.useUtils();
  const runningQuery = api.timeEntries.getRunning.useQuery(undefined, {
    refetchInterval: 30_000,
  });
  const clientsQuery = api.clients.getAll.useQuery();

  const [clientId, setClientId] = useState(defaultClientId);
  const [invoiceId, setInvoiceId] = useState(defaultInvoiceId);
  const [description, setDescription] = useState("");
  const [rateText, setRateText] = useState("");
  const [startedAt, setStartedAt] = useState(() => new Date());

  const running = runningQuery.data;
  const elapsed = useRunningElapsed(running?.startedAt);
  const clients = clientsQuery.data ?? [];
  const activeClientId = running?.clientId ?? clientId;

  const billableQuery = api.invoices.getBillable.useQuery(
    activeClientId ? { clientId: activeClientId } : undefined,
  );
  const billableInvoices = billableQuery.data ?? [];

  const todayStart = useMemo(() => {
    const d = new Date();
    d.setHours(0, 0, 0, 0);
    return d;
  }, []);

  const todayQuery = api.timeEntries.getAll.useQuery({ from: todayStart });
  const scrollPadding = useTabBarScrollPadding();

  const clockIn = api.timeEntries.clockIn.useMutation({
    onSuccess: async () => {
      await utils.timeEntries.getRunning.invalidate();
    },
  });

  const updateRunning = api.timeEntries.updateRunning.useMutation({
    onSuccess: async () => {
      await Promise.all([
        utils.timeEntries.getRunning.invalidate(),
        utils.invoices.getBillable.invalidate(),
      ]);
    },
    onError: (err) => {
      Alert.alert("Could not update timer", err.message);
    },
  });

  const clockOut = api.timeEntries.clockOut.useMutation({
    onSuccess: async (data) => {
      await endTimeClockLiveActivity();
      const message = describeClockOutOutcome({
        outcome: data.outcome,
        hours: data.hours,
        rate: data.rate,
        invoice: data.invoice,
      });
      Alert.alert(
        data.outcome === "linked_to_invoice" ? "Time logged" : "Timer stopped",
        message,
      );
      await Promise.all([
        utils.timeEntries.getRunning.invalidate(),
        utils.timeEntries.getAll.invalidate(),
        utils.invoices.getAll.invalidate(),
        utils.invoices.getBillable.invalidate(),
        utils.dashboard.getStats.invalidate(),
      ]);
      setDescription("");
    },
  });

  useEffect(() => {
    if (!running) return;
    setClientId(running.clientId ?? "");
    setInvoiceId(running.invoiceId ?? "");
    setDescription(running.description);
    setRateText(running.rate != null ? String(running.rate) : "");
  }, [running]);

  useEffect(() => {
    if (running || !clientId || rateText) return;
    const client = clients.find((c) => c.id === clientId);
    if (client?.defaultHourlyRate) {
      setRateText(String(client.defaultHourlyRate));
    }
  }, [clientId, clients, rateText, running]);

  useEffect(() => {
    if (!running) {
      void endTimeClockLiveActivity();
      return;
    }

    const sync = () => {
      const seconds = Math.floor(
        (Date.now() - new Date(running.startedAt).getTime()) / 1000,
      );
      void syncTimeClockLiveActivity(
        { ...running, description },
        seconds,
      );
    };

    sync();
    const interval = setInterval(sync, 30_000);
    return () => clearInterval(interval);
  }, [running, description]);

  const rate = parseFloat(rateText) || 0;
  const displayRate = running ? (running.rate ?? 0) : rate;

  const clientOptions = useMemo(
    () => clients.map((client) => ({ label: client.name, value: client.id })),
    [clients],
  );

  const invoiceOptions = useMemo(
    () => [
      { label: "No invoice — save entry only", value: "" },
      ...billableInvoices.map((invoice) => ({
        label: `${invoice.invoicePrefix ?? "#"}${invoice.invoiceNumber} (${invoice.status})`,
        value: invoice.id,
      })),
    ],
    [billableInvoices],
  );

  async function handleClockIn() {
    try {
      const backdated =
        Math.abs(Date.now() - startedAt.getTime()) > 60_000 ? startedAt : undefined;
      await clockIn.mutateAsync({
        description: description.trim(),
        clientId: clientId || "",
        invoiceId: invoiceId || undefined,
        rate: rate || undefined,
        startedAt: backdated,
      });
      setStartedAt(new Date());
    } catch (err) {
      Alert.alert("Clock in failed", err instanceof Error ? err.message : "Try again");
    }
  }

  async function handleClockOut() {
    try {
      await clockOut.mutateAsync({ description: description.trim() || undefined });
    } catch (err) {
      Alert.alert("Clock out failed", err instanceof Error ? err.message : "Try again");
    }
  }

  async function handleRunningClientChange(nextClientId: string) {
    if (!running) return;
    setClientId(nextClientId);
    setInvoiceId("");
    try {
      await updateRunning.mutateAsync({ clientId: nextClientId, invoiceId: "" });
      const client = clients.find((c) => c.id === nextClientId);
      if (client?.defaultHourlyRate != null) {
        setRateText(String(client.defaultHourlyRate));
      }
    } catch {
      setClientId(running.clientId ?? "");
      setInvoiceId(running.invoiceId ?? "");
    }
  }

  async function handleRunningInvoiceChange(nextInvoiceId: string) {
    if (!running) return;
    const previous = invoiceId;
    setInvoiceId(nextInvoiceId);
    try {
      await updateRunning.mutateAsync({ invoiceId: nextInvoiceId });
    } catch {
      setInvoiceId(previous);
    }
  }

  if (runningQuery.isLoading || clientsQuery.isLoading) {
    return <LoadingScreen message="Loading time clock…" />;
  }

  const todayEntries = (todayQuery.data ?? []).filter((entry) => entry.endedAt);
  const runningMeta = [
    running?.client?.name ?? (running ? "No client" : null),
    running?.invoice
      ? `${running.invoice.invoicePrefix ?? "#"}${running.invoice.invoiceNumber}`
      : null,
    displayRate ? `$${displayRate}/hr` : null,
  ]
    .filter(Boolean)
    .join(" · ");

  return (
    <ScrollView
      style={styles.scroll}
      contentContainerStyle={[tabLayout.scrollContent, { paddingBottom: scrollPadding }]}
      contentInsetAdjustmentBehavior={Platform.OS === "ios" ? "never" : undefined}
      scrollIndicatorInsets={{ bottom: scrollPadding }}
      refreshControl={
        <RefreshControl
          refreshing={runningQuery.isRefetching}
          onRefresh={() => {
            void runningQuery.refetch();
            void clientsQuery.refetch();
            void billableQuery.refetch();
            void todayQuery.refetch();
          }}
          tintColor={colors.primary}
        />
      }
    >
      {header}
      <View style={tabLayout.scrollBody}>
      {running || !compact ? (
        <GlassSurface style={running ? styles.runningCard : undefined}>
          <View style={styles.hero}>
            {running ? (
              <>
                <View style={styles.heroHeader}>
                  <View style={styles.pulseDot} />
                  <Text style={styles.heroLabel}>Running</Text>
                </View>
                <Text style={styles.timerValue}>{formatElapsedSeconds(elapsed)}</Text>
                <Text style={styles.runningTitle}>
                  {description.trim() || "No description"}
                </Text>
                <Text style={styles.runningMeta}>
                  Started {formatDateTime(running.startedAt)}
                  {runningMeta ? ` · ${runningMeta}` : ""}
                </Text>
              </>
            ) : (
              <Text style={styles.idleHint}>Track billable time and link it to invoices.</Text>
            )}
          </View>
        </GlassSurface>
      ) : null}

      {running ? (
        <Card style={styles.formCard}>
          <View style={styles.formFields}>
            <SelectField
              label="Client"
              placeholder="Select client…"
              value={clientId}
              options={clientOptions}
              disabled={updateRunning.isPending}
              onValueChange={(next) => void handleRunningClientChange(next)}
            />

            <SelectField
              label="Invoice"
              placeholder="No invoice — save entry only"
              value={invoiceId}
              options={invoiceOptions}
              disabled={updateRunning.isPending}
              onValueChange={(next) => void handleRunningInvoiceChange(next)}
            />

            <Input
              label="Description"
              value={description}
              onChangeText={setDescription}
              placeholder="What are you working on?"
            />
          </View>
        </Card>
      ) : (
        <Card style={styles.formCard}>
          <View style={styles.formFields}>
            <SelectField
              label="Client"
              placeholder="Select client…"
              value={clientId}
              options={clientOptions}
              onValueChange={(next) => {
                setClientId(next);
                setInvoiceId("");
                const client = clients.find((c) => c.id === next);
                setRateText(
                  client?.defaultHourlyRate != null ? String(client.defaultHourlyRate) : "",
                );
              }}
            />

            <SelectField
              label="Invoice"
              placeholder="No invoice — save entry only"
              value={invoiceId}
              options={invoiceOptions}
              disabled={!clientId}
              onValueChange={setInvoiceId}
            />

            <Input
              label="Description"
              value={description}
              onChangeText={setDescription}
              placeholder="What are you working on?"
            />

            <Input
              label="Hourly rate"
              value={rateText}
              onChangeText={setRateText}
              keyboardType="decimal-pad"
              placeholder="0.00"
            />

            <DateTimeField
              label="Started at"
              value={startedAt}
              maximumDate={new Date()}
              onChange={setStartedAt}
            />
            <Text style={styles.startedHint}>
              Set an earlier time if you forgot to clock in when you started working.
            </Text>
          </View>
        </Card>
      )}

      {running ? (
        <Button
          title="Clock out"
          variant="danger"
          loading={clockOut.isPending}
          onPress={handleClockOut}
        />
      ) : (
        <Button title="Clock in" loading={clockIn.isPending} onPress={handleClockIn} />
      )}

      {todayEntries.length > 0 ? (
        <Card title="Today">
          {todayEntries.map((entry) => {
            const invoiceLabel = entry.invoice
              ? `${entry.invoice.invoicePrefix ?? "#"}${entry.invoice.invoiceNumber}`
              : null;

            const row = (
              <>
                <View style={styles.entryMeta}>
                  <Text style={styles.entryTitle}>{entry.description || "No description"}</Text>
                  <Text style={styles.entrySub}>
                    {entry.client?.name ?? "No client"}
                    {invoiceLabel ? ` · ${invoiceLabel}` : " · not billed"}
                  </Text>
                </View>
                <Text style={styles.entryHours}>{entry.hours ?? "—"}h</Text>
              </>
            );

            if (!entry.invoice) {
              return (
                <View key={entry.id} style={styles.entryRow}>
                  {row}
                </View>
              );
            }

            return (
              <Pressable
                key={entry.id}
                accessibilityRole="button"
                accessibilityLabel={`View invoice ${invoiceLabel}`}
                onPress={() => router.push(`/(app)/invoices/${entry.invoice!.id}`)}
                style={({ pressed }) => [styles.entryRow, pressed && styles.entryRowPressed]}
              >
                {row}
              </Pressable>
            );
          })}
        </Card>
      ) : null}
      </View>
    </ScrollView>
  );
}

const createTimeClockStyles = (colors: ThemeColors, isDark: boolean) =>
  StyleSheet.create({
  scroll: {
    flex: 1,
  },
  runningCard: {
    borderColor: isDark ? "rgba(74, 222, 128, 0.35)" : "rgba(26, 26, 26, 0.18)",
  },
  hero: {
    padding: spacing.md,
    gap: spacing.sm,
  },
  heroHeader: {
    flexDirection: "row",
    alignItems: "center",
    gap: spacing.sm,
  },
  pulseDot: {
    width: 8,
    height: 8,
    borderRadius: 4,
    backgroundColor: colors.primary,
  },
  heroLabel: {
    fontSize: 13,
    fontFamily: fonts.bodyMedium,
    color: colors.mutedForeground,
    textTransform: "uppercase",
    letterSpacing: 0.4,
  },
  timerValue: {
    fontSize: 52,
    lineHeight: 56,
    fontFamily: fonts.mono,
    color: colors.foreground,
    fontVariant: ["tabular-nums"],
  },
  runningTitle: {
    fontSize: 16,
    fontFamily: fonts.bodySemiBold,
    color: colors.foreground,
  },
  runningMeta: {
    fontSize: 13,
    fontFamily: fonts.body,
    color: colors.mutedForeground,
  },
  idleHint: {
    fontSize: 14,
    fontFamily: fonts.body,
    color: colors.mutedForeground,
    marginTop: spacing.xs,
  },
  startedHint: {
    fontSize: 12,
    fontFamily: fonts.body,
    color: colors.mutedForeground,
    lineHeight: 18,
    marginTop: -spacing.xs,
  },
  formCard: {
    gap: 0,
  },
  formFields: {
    gap: spacing.md,
  },
  entryRow: {
    flexDirection: "row",
    justifyContent: "space-between",
    gap: spacing.md,
    paddingVertical: spacing.sm,
    borderTopWidth: 1,
    borderTopColor: colors.border,
  },
  entryRowPressed: {
    opacity: 0.65,
  },
  entryMeta: {
    flex: 1,
    gap: 2,
  },
  entryTitle: {
    fontFamily: fonts.bodySemiBold,
    color: colors.foreground,
    fontSize: 14,
  },
  entrySub: {
    fontFamily: fonts.body,
    color: colors.mutedForeground,
    fontSize: 12,
  },
  entryHours: {
    fontFamily: fonts.bodySemiBold,
    color: colors.foreground,
    fontSize: 14,
  },
});
