import dayjs, { Dayjs } from 'dayjs';
import { LinearGradient } from 'expo-linear-gradient';
import React, { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';

import { Colors } from '@/constants/Colors';
import { fetchMenstrualFlowSamples, healthDataEvents } from '@/utils/health';
import {
  buildMenstrualTimeline,
  convertHealthKitSamplesToCycleRecords,
  CycleRecord,
  DEFAULT_PERIOD_LENGTH,
  MenstrualDayInfo,
  MenstrualDayStatus,
  MenstrualTimeline,
} from '@/utils/menstrualCycle';

type Props = {
  onPress?: () => void;
};

type Summary = {
  state: string;
  prefix?: string;
  suffix?: string;
  number?: number;
  fallbackText: string;
};

const RingIcon = () => (
  <View style={styles.iconWrapper}>
    <LinearGradient
      colors={['#f572a7', '#f0a4ff', '#6f6ced']}
      start={{ x: 0, y: 0 }}
      end={{ x: 1, y: 1 }}
      style={styles.iconGradient}
    >
      <View style={styles.iconInner} />
    </LinearGradient>
  </View>
);

export const MenstrualCycleCard: React.FC<Props> = ({ onPress }) => {
  const { t } = useTranslation();
  const [records, setRecords] = useState<CycleRecord[]>([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    let mounted = true;
    const loadMenstrualData = async () => {
      // Avoid setting loading to true for background updates to prevent UI flicker
      if (records.length === 0) {
        setLoading(true);
      }
      try {
        const today = dayjs();
        const startDate = today.subtract(3, 'month').startOf('month').toDate();
        const endDate = today.add(4, 'month').endOf('month').toDate();

        const samples = await fetchMenstrualFlowSamples(startDate, endDate);
        if (!mounted) return;
        const converted = convertHealthKitSamplesToCycleRecords(samples);
        setRecords(converted);
      } catch (error) {
        console.error('Failed to load menstrual flow samples', error);
        if (mounted) {
          setRecords([]);
        }
      } finally {
        if (mounted) {
          setLoading(false);
        }
      }
    };

    loadMenstrualData();

    // Listen for data changes
    const handleDataChange = () => {
      loadMenstrualData();
    };
    healthDataEvents.on('menstrualDataChanged', handleDataChange);

    return () => {
      mounted = false;
      healthDataEvents.off('menstrualDataChanged', handleDataChange);
    };
  }, []);

  const timeline = useMemo(
    () =>
      buildMenstrualTimeline({
        records,
        monthsBefore: 2,
        monthsAfter: 4,
        defaultPeriodLength: DEFAULT_PERIOD_LENGTH,
      }),
    [records]
  );

  const summary = useMemo(() => {
    if (loading && records.length === 0) {
      return {
        state: t('menstrual.card.syncingState'),
        fallbackText: t('menstrual.card.syncingDesc'),
      };
    }

    return deriveSummary(timeline, records.length > 0, t);
  }, [loading, records.length, timeline, t]);

  return (
    <TouchableOpacity activeOpacity={0.92} onPress={onPress} style={styles.wrapper}>
      <View style={styles.headerRow}>
        <RingIcon />
        <Text style={styles.title}>{t('menstrual.card.title')}</Text>
        <View style={styles.badgeOuter}>
          <View style={styles.badgeInner} />
        </View>
      </View>
      <View style={styles.content}>
        <Text style={styles.stateText}>{summary.state}</Text>
        <Text style={styles.dayRow}>
          {summary.number !== undefined ? (
            <>
              {summary.prefix}
              <Text style={styles.dayNumber}>{summary.number}</Text>
              {summary.suffix}
            </>
          ) : (
            summary.fallbackText
          )}
        </Text>
      </View>
    </TouchableOpacity>
  );
};

const periodStatuses = new Set<MenstrualDayStatus>(['period', 'predicted-period']);
const fertileStatuses = new Set<MenstrualDayStatus>(['fertile', 'ovulation-day']);
const ovulationStatuses = new Set<MenstrualDayStatus>(['ovulation-day']);

const deriveSummary = (
  timeline: MenstrualTimeline,
  hasRecords: boolean,
  t: (key: string, options?: Record<string, any>) => string
): Summary => {
  const today = dayjs();
  const { dayMap, todayInfo } = timeline;

  if (!hasRecords || !Object.keys(dayMap).length) {
    return {
      state: t('menstrual.card.emptyState'),
      fallbackText: t('menstrual.card.emptyDesc'),
    };
  }

  const sortedInfos = Object.values(dayMap).sort(
    (a, b) => a.date.valueOf() - b.date.valueOf()
  );

  const findContinuousRange = (
    date: Dayjs,
    targetStatuses: Set<MenstrualDayStatus>
  ): { start: Dayjs; end: Dayjs } | null => {
    const key = date.format('YYYY-MM-DD');
    if (!targetStatuses.has(dayMap[key]?.status)) return null;

    let start = date;
    let end = date;

    while (true) {
      const prev = start.subtract(1, 'day');
      const prevInfo = dayMap[prev.format('YYYY-MM-DD')];
      if (prevInfo && targetStatuses.has(prevInfo.status)) {
        start = prev;
      } else {
        break;
      }
    }

    while (true) {
      const next = end.add(1, 'day');
      const nextInfo = dayMap[next.format('YYYY-MM-DD')];
      if (nextInfo && targetStatuses.has(nextInfo.status)) {
        end = next;
      } else {
        break;
      }
    }

    return { start, end };
  };

  const findFutureStatus = (
    targetStatuses: Set<MenstrualDayStatus>,
    inclusive = true
  ): MenstrualDayInfo | undefined => {
    return sortedInfos.find((info) => {
      const isInRange = inclusive
        ? !info.date.isBefore(today, 'day')
        : info.date.isAfter(today, 'day');
      return isInRange && targetStatuses.has(info.status);
    });
  };

  const findPastStatus = (targetStatuses: Set<MenstrualDayStatus>) => {
    for (let i = sortedInfos.length - 1; i >= 0; i -= 1) {
      const info = sortedInfos[i];
      if (!info.date.isAfter(today, 'day') && targetStatuses.has(info.status)) {
        return info;
      }
    }
    return undefined;
  };

  if (todayInfo && periodStatuses.has(todayInfo.status)) {
    const range = findContinuousRange(today, periodStatuses);
    const end = range?.end ?? today;
    const daysLeft = Math.max(end.diff(today, 'day'), 0);

    if (daysLeft === 0) {
      return {
        state:
          todayInfo.status === 'period'
            ? t('menstrual.card.periodState')
            : t('menstrual.card.predictedPeriodState'),
        fallbackText: t('menstrual.card.periodEndToday', {
          date: end.format(t('menstrual.dateFormatShort')),
        }),
      };
    }

    return {
      state:
        todayInfo.status === 'period'
          ? t('menstrual.card.periodState')
          : t('menstrual.card.predictedPeriodState'),
      prefix: t('menstrual.card.periodEndPrefix'),
      number: daysLeft,
      suffix: t('menstrual.card.periodEndSuffix', {
        date: end.format(t('menstrual.dateFormatShort')),
      }),
      fallbackText: '',
    };
  }

  const nextPeriod = findFutureStatus(periodStatuses, false);
  const lastPeriodInfo = findPastStatus(periodStatuses);
  const lastPeriodStart = lastPeriodInfo
    ? findContinuousRange(lastPeriodInfo.date, periodStatuses)?.start
    : undefined;

  const ovulationThisCycle = sortedInfos.find((info) => {
    if (!ovulationStatuses.has(info.status)) return false;
    if (lastPeriodStart && info.date.isBefore(lastPeriodStart, 'day')) return false;
    if (nextPeriod && !info.date.isBefore(nextPeriod.date, 'day')) return false;
    return true;
  });

  if (todayInfo?.status === 'fertile') {
    const targetOvulation = ovulationThisCycle ?? findFutureStatus(ovulationStatuses);
    if (targetOvulation) {
      const days = Math.max(targetOvulation.date.diff(today, 'day'), 0);
      if (days === 0) {
        return {
          state: t('menstrual.card.fertileState'),
          fallbackText: t('menstrual.card.ovulationToday'),
        };
      }
      return {
        state: t('menstrual.card.fertileState'),
        prefix: t('menstrual.card.ovulationCountdownPrefix'),
        number: days,
        suffix: t('menstrual.card.ovulationCountdownSuffix'),
        fallbackText: '',
      };
    }
  }

  const nextFertile = findFutureStatus(fertileStatuses);
  if (nextFertile && (!nextPeriod || nextFertile.date.isBefore(nextPeriod.date))) {
    const days = Math.max(nextFertile.date.diff(today, 'day'), 0);
    if (days === 0) {
      return {
        state: t('menstrual.card.fertileState'),
        fallbackText: t('menstrual.card.fertileToday'),
      };
    }
    return {
      state: t('menstrual.card.fertileState'),
      prefix: t('menstrual.card.fertileCountdownPrefix'),
      number: days,
      suffix: t('menstrual.card.fertileCountdownSuffix'),
      fallbackText: '',
    };
  }

  if (
    ovulationThisCycle &&
    nextPeriod &&
    today.isAfter(ovulationThisCycle.date, 'day') &&
    today.isBefore(nextPeriod.date, 'day')
  ) {
    const days = Math.max(nextPeriod.date.diff(today, 'day'), 0);
    return {
      state: t('menstrual.card.periodState'),
      prefix: t('menstrual.card.nextPeriodPrefix'),
      number: days,
      suffix: t('menstrual.card.nextPeriodSuffix'),
      fallbackText: '',
    };
  }

  if (nextPeriod) {
    const days = Math.max(nextPeriod.date.diff(today, 'day'), 0);
    return {
      state: t('menstrual.card.periodState'),
      prefix: t('menstrual.card.nextPeriodPrefix'),
      number: days,
      suffix: t('menstrual.card.nextPeriodSuffix'),
      fallbackText: '',
    };
  }

  return {
    state: t('menstrual.card.emptyState'),
    fallbackText: t('menstrual.card.emptyDesc'),
  };
};

const styles = StyleSheet.create({
  wrapper: {
    width: '100%',
  },
  headerRow: {
    flexDirection: 'row',
    alignItems: 'center',
    gap: 10,
  },
  iconWrapper: {
    width: 24,
    height: 24,
    borderRadius: 12,
    alignItems: 'center',
    justifyContent: 'center',
  },
  iconGradient: {
    width: 22,
    height: 22,
    borderRadius: 11,
    alignItems: 'center',
    justifyContent: 'center',
  },
  iconInner: {
    width: 10,
    height: 10,
    borderRadius: 5,
    backgroundColor: '#fff',
  },
  title: {
    fontSize: 14,
    color: '#192126',
    fontWeight: '600',
    flex: 1,
    fontFamily: 'AliBold',
  },
  badgeOuter: {
    width: 18,
    height: 18,
    borderRadius: 9,
    borderWidth: 2,
    borderColor: '#fbcfe8',
    alignItems: 'center',
    justifyContent: 'center',
  },
  badgeInner: {
    width: 6,
    height: 6,
    borderRadius: 3,
    backgroundColor: Colors.light.primary,
    opacity: 0.35,
  },
  content: {
    marginTop: 12,
  },
  stateText: {
    fontSize: 12,
    color: '#515558',
    marginBottom: 4,
    fontFamily: 'AliRegular',
  },
  dayRow: {
    fontSize: 14,
    color: '#192126',
    fontFamily: 'AliRegular',
  },
  dayNumber: {
    fontSize: 18,
    fontWeight: '700',
    color: '#192126',
    fontFamily: 'AliBold',
  },
});
