import { computed, shallowRef } from 'vue';
import { useEntity, useMutation, useQuery } from 'vue-sync-engine';
import {
  ProfileEntity,
  WeightEntity,
  daySummariesQuery,
  logWeightMutation,
  removeWeightMutation,
  weightsQuery,
} from '../data/defs';
import type { DaySummary } from '../data/defs';
import { useEntities } from '../data/composables';
import { dayShort, dayTitle, lastDays, todayISO } from '../domain/dates';
import { fmtG, fmtKcal } from '../domain/format';
import { PROFILE_ID } from '../domain/types';
import type { WeightLog } from '../domain/types';
import { activeTab, selectedDate } from '../ui/state';
import { IconScale, IconTrash } from '../ui/icons';

const PERIODS = [7, 14, 30] as const;

export default function StatsScreen() {
  const profile = useEntity(ProfileEntity, () => PROFILE_ID);
  const summariesQ = useQuery(daySummariesQuery, () => undefined);
  const weightsQ = useQuery(weightsQuery, () => undefined);
  const weights = useEntities(WeightEntity, () => weightsQ.data.value?.ids);

  const logWeight = useMutation(logWeightMutation);
  const removeWeight = useMutation(removeWeightMutation);

  const period = shallowRef<7 | 14 | 30>(14);
  const selectedBar = shallowRef(todayISO());

  const target = computed(() => profile.value?.targetKcal ?? 2000);

  const days = computed(() => {
    const byDate = new Map((summariesQ.data.value ?? []).map(day => [day.date, day]));
    return lastDays(period.value).map(date =>
      byDate.get(date) ?? { date, kcal: 0, protein: 0, fat: 0, carbs: 0, entries: 0 },
    );
  });

  const chartMax = computed(() => {
    const peak = Math.max(target.value, ...days.value.map(day => day.kcal));
    return peak * 1.08;
  });

  const selectedDay = computed(() =>
    days.value.find(day => day.date === selectedBar.value) ?? null,
  );

  const tracked = computed(() => days.value.filter(day => day.entries > 0));
  const averages = computed(() => {
    const list = tracked.value;
    if (list.length === 0) return null;
    const total = list.reduce(
      (acc, day) => ({ kcal: acc.kcal + day.kcal, protein: acc.protein + day.protein }),
      { kcal: 0, protein: 0 },
    );
    const onTarget = list.filter(day => day.kcal <= target.value).length;
    return {
      kcal: total.kcal / list.length,
      protein: total.protein / list.length,
      onTargetShare: Math.round((onTarget / list.length) * 100),
    };
  });

  // ── вес ───────────────────────────────────────────────────────────────────
  const weightInput = shallowRef(0);
  const latestWeight = computed(() => weights.value.at(-1) ?? null);
  const weightDelta = computed(() => {
    const list = weights.value;
    const latest = list.at(-1);
    if (!latest || list.length < 2) return null;
    const weekAgoDate = lastDays(8)[0] ?? latest.date;
    const reference = [...list].reverse().find(item => item.date <= weekAgoDate) ?? list[0];
    if (!reference || reference.id === latest.id) return null;
    return latest.kg - reference.kg;
  });

  const sparkPoints = computed(() => {
    const list = weights.value.slice(-30);
    if (list.length < 2) return '';
    const min = Math.min(...list.map(item => item.kg));
    const max = Math.max(...list.map(item => item.kg));
    const span = Math.max(max - min, 0.5);
    return list
      .map((item, index) => {
        const x = (index / (list.length - 1)) * 100;
        const y = 26 - ((item.kg - min) / span) * 22 + 2;
        return `${x.toFixed(1)},${y.toFixed(1)}`;
      })
      .join(' ');
  });

  const submitWeight = () => {
    if (weightInput.value <= 0) return;
    const date = todayISO();
    const weight: WeightLog = { id: date, date, kg: weightInput.value, createdAt: Date.now() };
    logWeight.mutate({ weight });
    weightInput.value = 0;
  };

  const openInDiary = (day: DaySummary) => {
    selectedDate.value = day.date;
    activeTab.value = 'diary';
  };

  const numeric = (raw: string): number => {
    const value = Number(raw.replace(',', '.'));
    return Number.isFinite(value) && value >= 0 ? value : 0;
  };

  return (
    <div class="flex flex-col gap-5 pb-6">
      <div class="animate-rise flex items-center justify-between">
        <h1 class="text-display text-xl font-medium">Статистика</h1>
        <div class="flex gap-1 rounded-full border hairline bg-surface/60 p-1">
          {PERIODS.map(value => (
            <button
              type="button"
              class={`rounded-full px-3 py-1 text-[12px] transition ${period.value === value ? 'bg-ember/20 text-ember-bright' : 'text-ink-faint hover:text-ink'}`}
              onClick={() => (period.value = value)}
            >
              {value}
              {' '}
              дн
            </button>
          ))}
        </div>
      </div>

      {/* График калорий по дням */}
      <section class="animate-rise rounded-3xl border hairline bg-surface/80 p-5" style={{ animationDelay: '40ms' }}>
        <div class="mb-3 flex items-baseline justify-between">
          <h2 class="text-[13px] text-ink-soft">Калории по дням</h2>
          <span class="text-[12px] text-ink-faint tnum">
            {`цель ${fmtKcal(target.value)}`}
          </span>
        </div>

        {selectedDay.value && (
          <button
            type="button"
            class="mb-3 flex w-full items-baseline justify-between rounded-xl bg-raised/50 px-3.5 py-2 text-left transition hover:bg-raised"
            onClick={() => selectedDay.value && openInDiary(selectedDay.value)}
          >
            <span class="text-[13px] text-ink-soft">{dayTitle(selectedDay.value.date)}</span>
            <span class="text-[13px] tnum">
              {selectedDay.value.entries > 0
                ? (
                    <span>
                      {fmtKcal(selectedDay.value.kcal)}
                      {' '}
                      ккал · Б
                      {' '}
                      {fmtG(selectedDay.value.protein)}
                    </span>
                  )
                : <span class="text-ink-faint">нет записей</span>}
            </span>
          </button>
        )}

        <div class="relative h-36">
          {/* Линия цели */}
          <div
            class="absolute right-0 left-0 z-10 border-t border-dashed border-ink-soft/40"
            style={{ bottom: `${(target.value / chartMax.value) * 100}%` }}
          />
          <div class="flex h-full items-end gap-[3px]">
            {days.value.map(day => (
              <button
                type="button"
                aria-label={`${day.date}: ${Math.round(day.kcal)} ккал`}
                class="group flex h-full flex-1 flex-col items-center justify-end"
                onClick={() => (selectedBar.value = day.date)}
              >
                <div
                  class={`w-full rounded-t-[4px] transition-colors ${day.kcal > 0 ? '' : 'min-h-[2px]'} ${selectedBar.value === day.date
                    ? 'bg-ember-bright'
                    : day.kcal > target.value ? 'bg-over/80 group-hover:bg-over' : 'bg-ember/65 group-hover:bg-ember'}`}
                  style={{ height: `${Math.max((day.kcal / chartMax.value) * 100, day.kcal > 0 ? 2 : 1)}%` }}
                />
              </button>
            ))}
          </div>
        </div>
        <div class="mt-1.5 flex justify-between text-[10px] text-ink-faint tnum">
          <span>{dayShort(days.value[0]?.date ?? todayISO())}</span>
          <span>{dayShort(days.value.at(-1)?.date ?? todayISO())}</span>
        </div>
      </section>

      {/* Средние за период */}
      {averages.value && (
        <section class="animate-rise grid grid-cols-3 gap-2" style={{ animationDelay: '80ms' }}>
          <div class="rounded-2xl border hairline bg-surface/60 px-3 py-3 text-center">
            <div class="text-display text-[22px] font-light tnum">{fmtKcal(averages.value.kcal)}</div>
            <div class="mt-0.5 text-[11px] text-ink-faint">ккал в среднем</div>
          </div>
          <div class="rounded-2xl border hairline bg-surface/60 px-3 py-3 text-center">
            <div class="text-display text-[22px] font-light tnum">{fmtG(averages.value.protein)}</div>
            <div class="mt-0.5 text-[11px] text-ink-faint">белка в день, г</div>
          </div>
          <div class="rounded-2xl border hairline bg-surface/60 px-3 py-3 text-center">
            <div class="text-display text-[22px] font-light tnum">
              {averages.value.onTargetShare}
              %
            </div>
            <div class="mt-0.5 text-[11px] text-ink-faint">дней в цели</div>
          </div>
        </section>
      )}

      {/* Вес */}
      <section class="animate-rise rounded-3xl border hairline bg-surface/80 p-5" style={{ animationDelay: '120ms' }}>
        <div class="mb-3 flex items-center gap-2">
          <IconScale class="size-4.5 text-ink-soft" />
          <h2 class="flex-1 text-[13px] text-ink-soft">Вес</h2>
          {weightDelta.value !== null && (
            <span class={`text-[12px] tnum ${weightDelta.value <= 0 ? 'text-protein' : 'text-ink-faint'}`}>
              {weightDelta.value > 0 ? '+' : ''}
              {fmtG(weightDelta.value)}
              {' '}
              кг за неделю
            </span>
          )}
        </div>

        {latestWeight.value && (
          <div class="mb-3 flex items-end justify-between">
            <div>
              <span class="text-display text-[36px] leading-none font-light tnum">{fmtG(latestWeight.value.kg)}</span>
              <span class="ml-1 text-[13px] text-ink-faint">кг</span>
            </div>
            {sparkPoints.value && (
              <svg class="h-8 w-36" viewBox="0 0 100 30" preserveAspectRatio="none">
                <polyline
                  points={sparkPoints.value}
                  fill="none"
                  stroke="var(--color-ember-bright)"
                  stroke-width="1.6"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                />
              </svg>
            )}
          </div>
        )}

        <div class="flex gap-2">
          <input
            type="number"
            inputmode="decimal"
            min="1"
            step="0.1"
            placeholder="Вес сегодня, кг"
            value={weightInput.value || ''}
            onInput={event => (weightInput.value = numeric(event.currentTarget.value))}
            class="w-full flex-1 rounded-xl border hairline bg-raised/70 px-3.5 py-2.5 text-[16px] text-ink outline-none transition focus:border-ember/50 placeholder:text-ink-faint tnum"
          />
          <button
            type="button"
            class="rounded-xl bg-ember px-4 text-[14px] font-medium text-[#1a1006] transition hover:bg-ember-bright disabled:opacity-40"
            disabled={weightInput.value <= 0}
            onClick={submitWeight}
          >
            Записать
          </button>
        </div>

        {weights.value.length > 0 && (
          <div class="mt-3 flex flex-col">
            {[...weights.value].reverse().slice(0, 5).map(item => (
              <div class="flex items-center gap-2 border-t hairline py-2 text-[13px]">
                <span class="flex-1 text-ink-faint">{dayTitle(item.date)}</span>
                <span class="tnum">
                  {fmtG(item.kg)}
                  {' '}
                  кг
                </span>
                <button
                  type="button"
                  aria-label="Удалить замер"
                  class="grid size-7 place-items-center rounded-full text-ink-faint transition hover:bg-over/15 hover:text-over-bright"
                  onClick={() => removeWeight.mutate({ id: item.id })}
                >
                  <IconTrash class="size-3.5" />
                </button>
              </div>
            ))}
          </div>
        )}

        <p class="mt-3 text-[12px] leading-relaxed text-ink-faint">
          Взвешивайтесь утром натощак. Смотрите на тренд за неделю, а не на
          ежедневные колебания — вода и еда в желудке шумят на ±1 кг.
        </p>
      </section>
    </div>
  );
}
