/**
 * 앱 전역 표시 시간대 (IANA).
 * DB app-settings · 설정 화면 · 하단 바에서 동일 값 사용.
 */
import { useSyncExternalStore } from 'react';
import { TickMarkType, type Time } from 'lightweight-charts';
import type { Timeframe } from '../types';

export const DEFAULT_DISPLAY_TIMEZONE = 'Asia/Seoul';

export interface TimezoneOption {
  id: string;
  label: string;
  abbr: string;
}

export const TIMEZONE_OPTIONS: TimezoneOption[] = [
  { id: 'Asia/Seoul',         label: 'Asia/Seoul (KST, UTC+9)',     abbr: 'KST' },
  { id: 'UTC',                label: 'UTC',                         abbr: 'UTC' },
  { id: 'Asia/Tokyo',         label: 'Asia/Tokyo (JST, UTC+9)',     abbr: 'JST' },
  { id: 'Asia/Shanghai',      label: 'Asia/Shanghai (CST, UTC+8)',  abbr: 'CST' },
  { id: 'Asia/Hong_Kong',     label: 'Asia/Hong_Kong (HKT, UTC+8)', abbr: 'HKT' },
  { id: 'Asia/Singapore',     label: 'Asia/Singapore (SGT, UTC+8)', abbr: 'SGT' },
  { id: 'Europe/London',      label: 'Europe/London (GMT/BST)',     abbr: 'GMT' },
  { id: 'Europe/Berlin',      label: 'Europe/Berlin (CET)',         abbr: 'CET' },
  { id: 'America/New_York',   label: 'America/New_York (ET)',       abbr: 'ET' },
  { id: 'America/Chicago',    label: 'America/Chicago (CT)',        abbr: 'CT' },
  { id: 'America/Los_Angeles',label: 'America/Los_Angeles (PT)',    abbr: 'PT' },
];

let _tz = DEFAULT_DISPLAY_TIMEZONE;
const _listeners = new Set<() => void>();

export function getDisplayTimezone(): string {
  return _tz;
}

export function setDisplayTimezone(tz: string): void {
  const next = normalizeTimezone(tz);
  if (_tz === next) return;
  _tz = next;
  _listeners.forEach(l => l());
}

export function subscribeDisplayTimezone(cb: () => void): () => void {
  _listeners.add(cb);
  return () => _listeners.delete(cb);
}

export function useDisplayTimezone(): string {
  return useSyncExternalStore(subscribeDisplayTimezone, getDisplayTimezone, () => DEFAULT_DISPLAY_TIMEZONE);
}

export function normalizeTimezone(tz: string): string {
  const t = (tz || '').trim();
  if (!t) return DEFAULT_DISPLAY_TIMEZONE;
  if (TIMEZONE_OPTIONS.some(o => o.id === t)) return t;
  try {
    Intl.DateTimeFormat(undefined, { timeZone: t });
    return t;
  } catch {
    return DEFAULT_DISPLAY_TIMEZONE;
  }
}

export function getTimezoneAbbr(tz: string): string {
  const id = normalizeTimezone(tz);
  return TIMEZONE_OPTIONS.find(o => o.id === id)?.abbr ?? id.split('/').pop() ?? 'TZ';
}

function isDailyTimeframe(tf?: string): boolean {
  return tf === '1D' || tf === '1W' || tf === '1M';
}

/** 하단 바·시계 등 HH:mm:ss */
export function formatUnixClock(unixSec: number, tz?: string, withSeconds = true): string {
  if (!unixSec) return '–';
  const zone = normalizeTimezone(tz ?? _tz);
  const d = new Date(unixSec * 1000);
  const opts: Intl.DateTimeFormatOptions = {
    timeZone: zone,
    hour: '2-digit',
    minute: '2-digit',
    hour12: false,
  };
  if (withSeconds) opts.second = '2-digit';
  return new Intl.DateTimeFormat('en-GB', opts).format(d);
}

/** 차트 축·범례용 */
export function formatUnixForChart(unixSec: number, timeframe?: Timeframe, tz?: string): string {
  if (!unixSec) return '';
  const zone = normalizeTimezone(tz ?? _tz);
  const d = new Date(unixSec * 1000);
  if (isDailyTimeframe(timeframe)) {
    return new Intl.DateTimeFormat('en-GB', {
      timeZone: zone,
      year: '2-digit',
      month: '2-digit',
      day: '2-digit',
    }).format(d);
  }
  return new Intl.DateTimeFormat('en-GB', {
    timeZone: zone,
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    hour12: false,
  }).format(d);
}

/** LWC Time → unix 초 (없으면 null) */
export function lwcTimeToUnix(time: Time): number | null {
  if (typeof time === 'number') return time;
  if (time && typeof time === 'object' && 'year' in time) {
    const t = time as { year: number; month: number; day: number };
    return Math.floor(Date.UTC(t.year, t.month - 1, t.day) / 1000);
  }
  return null;
}

/** LWC Time → 문자열 (크로스헤어 등) */
export function formatLwcTime(
  time: number | { year: number; month: number; day: number },
  timeframe?: Timeframe,
  tz?: string,
): string {
  if (typeof time === 'number') {
    return formatUnixForChart(time, timeframe, tz);
  }
  const zone = normalizeTimezone(tz ?? _tz);
  const utc = Date.UTC(time.year, time.month - 1, time.day);
  return new Intl.DateTimeFormat('en-GB', {
    timeZone: zone,
    year: '2-digit',
    month: '2-digit',
    day: '2-digit',
  }).format(new Date(utc));
}

/** LWC X축 눈금 — 선택한 표시 시간대 기준 (기본 포맷터는 브라우저 로컬 TZ 사용) */
export function formatLwcTickMark(
  time: Time,
  tickMarkType: TickMarkType,
  timeframe?: Timeframe,
  tz?: string,
): string {
  const zone = normalizeTimezone(tz ?? _tz);
  const unixSec = lwcTimeToUnix(time);
  if (unixSec == null) return '';
  const d = new Date(unixSec * 1000);

  switch (tickMarkType) {
    case TickMarkType.Year:
      return new Intl.DateTimeFormat('en-GB', { timeZone: zone, year: 'numeric' }).format(d);
    case TickMarkType.Month:
      return new Intl.DateTimeFormat('en-GB', { timeZone: zone, month: 'short' }).format(d);
    case TickMarkType.DayOfMonth:
      if (isDailyTimeframe(timeframe)) {
        return new Intl.DateTimeFormat('en-GB', {
          timeZone: zone,
          month: 'short',
          day: 'numeric',
        }).format(d);
      }
      return new Intl.DateTimeFormat('en-GB', { timeZone: zone, day: 'numeric' }).format(d);
    case TickMarkType.Time:
      return new Intl.DateTimeFormat('en-GB', {
        timeZone: zone,
        hour: '2-digit',
        minute: '2-digit',
        hour12: false,
      }).format(d);
    case TickMarkType.TimeWithSeconds:
      return new Intl.DateTimeFormat('en-GB', {
        timeZone: zone,
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
        hour12: false,
      }).format(d);
    default:
      return formatUnixForChart(unixSec, timeframe, zone);
  }
}

/** 하단 바 표시: `06:36:00 KST` */
export function formatBottomBarTime(unixSec: number, tz?: string): string {
  if (!unixSec) return `– ${getTimezoneAbbr(tz ?? _tz)}`;
  const zone = normalizeTimezone(tz ?? _tz);
  return `${formatUnixClock(unixSec, zone, true)} ${getTimezoneAbbr(zone)}`;
}

/** 알림·드로잉 등 날짜+시간 */
export function formatUnixDateTime(unixSec: number, tz?: string): string {
  if (!unixSec) return '—';
  const zone = normalizeTimezone(tz ?? _tz);
  const d = new Date(unixSec * 1000);
  const date = new Intl.DateTimeFormat('en-GB', {
    timeZone: zone,
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
  }).format(d);
  return `${date} ${formatUnixClock(unixSec, zone, true)}`;
}

/** 현재 시각 (호가창 시계 등) */
export function formatNowClock(tz?: string, withSeconds = true): string {
  const zone = normalizeTimezone(tz ?? _tz);
  return formatUnixClock(Math.floor(Date.now() / 1000), zone, withSeconds);
}
