/**
 * Kỳ báo cáo. Toàn hệ thống dùng chuỗi sắp xếp được thay vì đối tượng Date,
 * để so sánh và nhóm dữ liệu trong SQL không phụ thuộc múi giờ của máy chủ.
 *
 *   tháng: '2026-08'      quý: '2026-Q3'      năm: '2026'
 */

export type PeriodType = "MONTH" | "QUARTER" | "YEAR";

const TZ = process.env.APP_TIMEZONE ?? "Asia/Ho_Chi_Minh";

/** Ngày hôm nay theo giờ Việt Nam, dạng 'YYYY-MM-DD'. */
export function today(): string {
  return new Intl.DateTimeFormat("en-CA", {
    timeZone: TZ,
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
  }).format(new Date());
}

/** Ngày (YYYY-MM-DD) của một thời điểm theo giờ Việt Nam — dùng thay cho `toISOString().slice(0, 10)` vốn trả về ngày theo giờ quốc tế (UTC) và lệch một ngày từ 0h–7h sáng. */
export function dayInTz(moment: Date): string {
  return new Intl.DateTimeFormat("en-CA", { timeZone: TZ, year: "numeric", month: "2-digit", day: "2-digit" }).format(moment);
}

export function monthKey(isoDate: string): string {
  return isoDate.slice(0, 7);
}

export function quarterKey(isoDate: string): string {
  const year = isoDate.slice(0, 4);
  const month = Number(isoDate.slice(5, 7));
  return `${year}-Q${Math.ceil(month / 3)}`;
}

export function yearKey(isoDate: string): string {
  return isoDate.slice(0, 4);
}

/** Khoảng ngày [từ, đến] của một kỳ, dùng làm điều kiện WHERE. */
export function periodRange(periodKey: string): { start: string; end: string } {
  // Năm: '2026'
  if (/^\d{4}$/.test(periodKey)) {
    return { start: `${periodKey}-01-01`, end: `${periodKey}-12-31` };
  }
  // Quý: '2026-Q3'
  const quarter = periodKey.match(/^(\d{4})-Q([1-4])$/);
  if (quarter) {
    const year = Number(quarter[1]);
    const q = Number(quarter[2]);
    const startMonth = (q - 1) * 3 + 1;
    const endMonth = startMonth + 2;
    return {
      start: `${year}-${String(startMonth).padStart(2, "0")}-01`,
      end: lastDayOfMonth(year, endMonth),
    };
  }
  // Tháng: '2026-08'
  const month = periodKey.match(/^(\d{4})-(\d{2})$/);
  if (month) {
    const year = Number(month[1]);
    const m = Number(month[2]);
    return { start: `${periodKey}-01`, end: lastDayOfMonth(year, m) };
  }
  throw new Error(`Kỳ báo cáo không hợp lệ: ${periodKey}`);
}

function lastDayOfMonth(year: number, month: number): string {
  const day = new Date(Date.UTC(year, month, 0)).getUTCDate();
  return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
}

/** Lùi `count` tháng từ một kỳ tháng. shiftMonth('2026-01', 1) → '2025-12'. */
export function shiftMonth(periodKey: string, count: number): string {
  const [y, m] = periodKey.split("-").map(Number);
  const date = new Date(Date.UTC(y, m - 1 - count, 1));
  return `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, "0")}`;
}

/** Danh sách `count` kỳ tháng gần nhất tính ngược từ `periodKey`, cũ → mới. */
export function lastMonths(periodKey: string, count: number): string[] {
  return Array.from({ length: count }, (_, i) => shiftMonth(periodKey, count - 1 - i));
}

/** Thứ Hai của tuần chứa ngày đã cho. */
export function weekStart(isoDate: string): string {
  const date = new Date(`${isoDate}T00:00:00Z`);
  const dow = date.getUTCDay(); // 0 = Chủ nhật
  const diff = dow === 0 ? -6 : 1 - dow;
  date.setUTCDate(date.getUTCDate() + diff);
  return date.toISOString().slice(0, 10);
}

/** Các ngày trong tuần bắt đầu từ `monday`. */
export function weekDays(monday: string): string[] {
  return Array.from({ length: 7 }, (_, i) => {
    const date = new Date(`${monday}T00:00:00Z`);
    date.setUTCDate(date.getUTCDate() + i);
    return date.toISOString().slice(0, 10);
  });
}

/**
 * Danh sách các kỳ tháng nằm trong một kỳ bất kỳ.
 *
 *   '2026-08'  → ['2026-08']
 *   '2026-Q3'  → ['2026-07', '2026-08', '2026-09']
 *   '2026'     → 12 tháng của năm
 *
 * Dùng để cộng dồn những số liệu chỉ lưu theo tháng (chỉ tiêu, KPI) lên kỳ quý
 * hoặc năm, thay vì buộc người dùng nhập thêm một bộ số liệu riêng cho quý.
 */
export function monthsInPeriod(periodKey: string): string[] {
  if (/^\d{4}$/.test(periodKey)) {
    return Array.from({ length: 12 }, (_, i) => `${periodKey}-${String(i + 1).padStart(2, "0")}`);
  }

  const quarter = periodKey.match(/^(\d{4})-Q([1-4])$/);
  if (quarter) {
    const firstMonth = (Number(quarter[2]) - 1) * 3 + 1;
    return [0, 1, 2].map((i) => `${quarter[1]}-${String(firstMonth + i).padStart(2, "0")}`);
  }

  if (/^\d{4}-\d{2}$/.test(periodKey)) return [periodKey];

  throw new Error(`Kỳ báo cáo không hợp lệ: ${periodKey}`);
}

/** Kỳ quý chứa một kỳ tháng. '2026-08' → '2026-Q3' */
export function quarterOfMonth(monthPeriod: string): string {
  const year = monthPeriod.slice(0, 4);
  const month = Number(monthPeriod.slice(5, 7));
  return `${year}-Q${Math.ceil(month / 3)}`;
}

/**
 * Tỷ lệ thời gian đã trôi qua của một kỳ bất kỳ, tính theo ngày.
 * Dùng làm mốc so sánh cho tiến độ: đạt 40% kế hoạch khi mới qua 32% thời gian
 * là đang tốt, không phải đang báo động.
 */
export function periodElapsed(periodKey: string, asOf = today()): { elapsed: number; total: number } {
  const { start, end } = periodRange(periodKey);
  const dayCount = (from: string, to: string) =>
    Math.round(
      (Date.parse(`${to}T00:00:00Z`) - Date.parse(`${from}T00:00:00Z`)) / 86_400_000,
    ) + 1;

  const total = dayCount(start, end);
  if (asOf < start) return { elapsed: 0, total };
  if (asOf > end) return { elapsed: total, total };
  return { elapsed: dayCount(start, asOf), total };
}

/**
 * Tháng gần nhất đã kết thúc.
 *
 * Dùng cho mọi phép so sánh kỳ này với kỳ trước (cảnh báo sụt giảm, dự báo).
 * So tháng đang chạy dở với tháng trước đủ ngày luôn cho ra kết quả sai lệch.
 */
export function lastCompletedMonth(asOf = today()): string {
  return shiftMonth(monthKey(asOf), 1);
}

/** Số ngày đã trôi qua và tổng số ngày của tháng — dùng tính % tiến độ luỹ tiến. */
export function monthProgress(periodKey: string, asOf = today()): { elapsed: number; total: number } {
  const { start, end } = periodRange(periodKey);
  const total = Number(end.slice(8, 10));
  if (asOf < start) return { elapsed: 0, total };
  if (asOf > end) return { elapsed: total, total };
  return { elapsed: Number(asOf.slice(8, 10)), total };
}

/** YYYY-MM-DD có thật trên lịch (không nhận 31/02, tháng 13...). Dùng cho mọi tham số ngày do người dùng nhập/gõ trên URL. */
export function isRealDate(value: string | null | undefined): value is string {
  if (!value || !/^\d{4}-\d{2}-\d{2}$/.test(value)) return false;
  const parsed = Date.parse(`${value}T00:00:00Z`);
  return !Number.isNaN(parsed) && new Date(parsed).toISOString().slice(0, 10) === value;
}

/** YYYY-MM với tháng 01–12. */
export function isRealMonth(value: string | null | undefined): value is string {
  return !!value && /^\d{4}-(0[1-9]|1[0-2])$/.test(value);
}
