import { NextRequest } from "next/server";
import { readFile, writeFile, mkdir } from "node:fs/promises";
import { existsSync } from "node:fs";
import { join } from "node:path";

// ── Timezone abbreviation mapping ──────────────────────────────────────────
const TZ_ABBREVIATIONS: Record<string, string> = {
  "Asia/Kuala_Lumpur": "MYT",
  "Asia/Singapore": "SGT",
  "Asia/Jakarta": "WIB",
  "Asia/Makassar": "WITA",
  "Asia/Jayapura": "WIT",
  "Asia/Bangkok": "ICT",
  "Asia/Ho_Chi_Minh": "ICT",
  "Asia/Phnom_Penh": "ICT",
  "Asia/Vientiane": "ICT",
  "Asia/Yangon": "MMT",
  "Asia/Dhaka": "BST",
  "Asia/Kolkata": "IST",
  "Asia/Karachi": "PKT",
  "Asia/Dubai": "GST",
  "Asia/Riyadh": "AST",
  "Asia/Baghdad": "AST",
  "Asia/Tehran": "IRST",
  "Asia/Kabul": "AFT",
  "Asia/Tashkent": "UZT",
  "Europe/London": "GMT",
  "Europe/Istanbul": "TRT",
  "Europe/Berlin": "CET",
  "Europe/Paris": "CET",
  "America/New_York": "EST",
  "America/Chicago": "CST",
  "America/Denver": "MST",
  "America/Los_Angeles": "PST",
  "America/Toronto": "EST",
  "Africa/Cairo": "EET",
  "Africa/Johannesburg": "SAST",
  "Australia/Sydney": "AEDT",
  "Australia/Perth": "AWST",
  "Pacific/Auckland": "NZDT",
};

function getTimezoneAbbr(timezone: string): string {
  // Direct lookup first
  if (TZ_ABBREVIATIONS[timezone]) return TZ_ABBREVIATIONS[timezone];

  // Try to extract abbreviation dynamically from the IANA name
  // e.g., "America/New_York" -> "ET", "Asia/Kuala_Lumpur" -> "MYT"
  const parts = timezone.split("/");
  if (parts.length >= 2) {
    const region = parts[0];
    const city = parts[parts.length - 1].replace(/_/g, " ");
    // Common known abbreviations
    const known: Record<string, string> = {
      London: "GMT",
      Dublin: "GMT",
      Lisbon: "WET",
      Berlin: "CET",
      Paris: "CET",
      Rome: "CET",
      Madrid: "CET",
      Amsterdam: "CET",
      Brussels: "CET",
      Vienna: "CET",
      Stockholm: "CET",
      Oslo: "CET",
      Copenhagen: "CET",
      Warsaw: "CET",
      Budapest: "CET",
      Prague: "CET",
      Athens: "EET",
      Helsinki: "EET",
      Bucharest: "EET",
      Sofia: "EET",
      Moscow: "MSK",
      Istanbul: "TRT",
    };
    if (known[city]) return known[city];
  }

  // For Asia timezones, try to use country code from city name
  const countryFromCity: Record<string, string> = {
    "Kuala Lumpur": "MYT",
    "Singapore": "SGT",
    "Jakarta": "WIB",
    "Tokyo": "JST",
    "Seoul": "KST",
    "Hong_Kong": "HKT",
    "Taipei": "CST",
    "Beijing": "CST",
    "Shanghai": "CST",
    "Manila": "PHT",
    "Bangkok": "ICT",
    "Hanoi": "ICT",
    "New Delhi": "IST",
    "Colombo": "IST",
    "Kathmandu": "NPT",
    "Dhaka": "BST",
    "Islamabad": "PKT",
  };

  const cityKey = parts[parts.length - 1];
  if (countryFromCity[cityKey]) return countryFromCity[cityKey];

  // Fallback: return the timezone itself
  return timezone;
}

// ── Cache helpers ──────────────────────────────────────────────────────────
const CACHE_DIR = "/tmp/prayer-cache";
const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour

function cacheKey(city: string, country: string): string {
  return `${city.toLowerCase().replace(/\s+/g, "-")}-${country.toLowerCase().replace(/\s+/g, "-")}`;
}

function cachePath(key: string): string {
  return join(CACHE_DIR, `${key}.json`);
}

interface CacheEntry {
  cachedAt: number;
  data: AlAdhanResponse;
}

interface AlAdhanTimings {
  Fajr: string;
  Sunrise: string;
  Dhuhr: string;
  Asr: string;
  Sunset: string;
  Maghrib: string;
  Isha: string;
  Imsak: string;
  Midnight: string;
  Firstthird: string;
  Lastthird: string;
}

interface AlAdhanDateInfo {
  readable: string;
  timestamp: string;
  gregorian: {
    date: string;
    format: string;
    day: string;
    weekday: { en: string };
    month: { number: number; en: string };
    year: string;
    designation: { abbreviated: string; expanded: string };
  };
  hijri: {
    date: string;
    format: string;
    day: string;
    weekday: { en: string; ar: string };
    month: { number: number; en: string; ar: string };
    year: string;
    designation: { abbreviated: string; expanded: string };
    holidays: string[];
  };
}

interface AlAdhanMeta {
  latitude: number;
  longitude: number;
  timezone: string;
  method: { id: number; name: string };
  latitudeAdjustment: string;
  midnightMode: string;
  school: string;
  offset: Record<string, number>;
}

interface AlAdhanResponse {
  code: number;
  status: string;
  data: {
    timings: AlAdhanTimings;
    date: AlAdhanDateInfo;
    meta: AlAdhanMeta;
  };
}

interface PrayerTimings {
  Fajr: string;
  Sunrise?: string;
  Dhuhr: string;
  Asr: string;
  Sunset?: string;
  Maghrib: string;
  Isha: string;
  [key: string]: string | undefined;
}

interface PrayerResponse {
  timings: Record<string, string>;
  date: string;
  hijri: string;
  city: string;
  country: string;
  timezone: string;
}

// ── Fallback defaults ──────────────────────────────────────────────────────
function getFallbackResponse(city: string, country: string): PrayerResponse {
  const timezone = city === "Kuala Lumpur" && country === "Malaysia"
    ? "Asia/Kuala_Lumpur"
    : `Asia/${city.replace(/\s/g, "_")}`;
  const tzAbbr = getTimezoneAbbr(timezone);

  return {
    timings: {
      Fajr: `05:42 (${tzAbbr})`,
      Dhuhr: `13:15 (${tzAbbr})`,
      Asr: `16:30 (${tzAbbr})`,
      Maghrib: `19:22 (${tzAbbr})`,
      Isha: `20:39 (${tzAbbr})`,
    },
    date: new Date().toLocaleDateString("en-GB", {
      day: "numeric",
      month: "short",
      year: "numeric",
    }),
    hijri: "—",
    city,
    country,
    timezone,
  };
}

// ── Main handler ───────────────────────────────────────────────────────────
export const runtime = "nodejs";

export async function GET(request: NextRequest) {
  const searchParams = request.nextUrl.searchParams;
  const city = searchParams.get("city") || "Kuala Lumpur";
  const country = searchParams.get("country") || "Malaysia";
  const key = cacheKey(city, country);
  const cPath = cachePath(key);

  // 1. Try reading from cache
  try {
    if (existsSync(cPath)) {
      const raw = await readFile(cPath, "utf-8");
      const entry: CacheEntry = JSON.parse(raw);
      const age = Date.now() - entry.cachedAt;

      // If cache is still fresh, return it
      if (age < CACHE_TTL_MS) {
        return Response.json(formatResponse(entry.data, city, country));
      }

      // If cache is stale but we have it, we'll try to refresh but fall back to stale
      try {
        const fresh = await fetchAlAdhan(city, country);
        await saveCache(cPath, fresh);
        return Response.json(formatResponse(fresh, city, country));
      } catch {
        // API failed; return stale cache
        return Response.json(formatResponse(entry.data, city, country));
      }
    }
  } catch {
    // Cache read error — proceed to fetch fresh
  }

  // 2. No cache — fetch from alAdhan
  try {
    const data = await fetchAlAdhan(city, country);
    await saveCache(cPath, data);
    return Response.json(formatResponse(data, city, country));
  } catch (error) {
    console.error("Prayer times API error:", error);

    // 3. Try stale cache even if we already checked (race condition guard)
    try {
      if (existsSync(cPath)) {
        const raw = await readFile(cPath, "utf-8");
        const entry: CacheEntry = JSON.parse(raw);
        return Response.json(formatResponse(entry.data, city, country));
      }
    } catch {
      // ignore
    }

    // 4. Last resort: fallback defaults
    return Response.json(getFallbackResponse(city, country));
  }
}

// ── Helpers ────────────────────────────────────────────────────────────────

async function fetchAlAdhan(
  city: string,
  country: string
): Promise<AlAdhanResponse> {
  const url = `https://api.aladhan.com/v1/timingsByCity?city=${encodeURIComponent(city)}&country=${encodeURIComponent(country)}&method=2&adjustment=1`;
  const res = await fetch(url, {
    headers: { Accept: "application/json" },
    next: { revalidate: 3600 },
  });

  if (!res.ok) {
    throw new Error(`alAdhan API returned ${res.status}: ${await res.text()}`);
  }

  const json: AlAdhanResponse = await res.json();

  if (json.code !== 200 || json.status !== "OK") {
    throw new Error(`alAdhan API error: ${json.status} (code ${json.code})`);
  }

  return json;
}

async function saveCache(path: string, data: AlAdhanResponse): Promise<void> {
  try {
    await mkdir(CACHE_DIR, { recursive: true });
    const entry: CacheEntry = { cachedAt: Date.now(), data };
    await writeFile(path, JSON.stringify(entry), "utf-8");
  } catch (err) {
    console.error("Failed to write prayer cache:", err);
  }
}

function formatResponse(
  apiData: AlAdhanResponse,
  city: string,
  country: string
): PrayerResponse {
  const { timings, date, meta } = apiData.data;
  const tzAbbr = getTimezoneAbbr(meta.timezone);

  // The alAdhan API returns times like "05:42 (MST)" already if adjustment is set,
  // but we'll append our own timezone label to be safe
  const prayerOrder = ["Fajr", "Sunrise", "Dhuhr", "Asr", "Sunset", "Maghrib", "Isha", "Imsak", "Midnight"];
  const formattedTimings: Record<string, string> = {};

  for (const prayer of prayerOrder) {
    const time = (timings as unknown as Record<string, string>)[prayer];
    if (time) {
      // Strip any existing timezone label in parentheses and re-add ours
      const cleanTime = time.replace(/\s*\(.*?\)\s*/g, "").trim();
      formattedTimings[prayer] = `${cleanTime} (${tzAbbr})`;
    }
  }

  return {
    timings: formattedTimings,
    date: date.readable,
    hijri: `${date.hijri.day} ${date.hijri.month.en} ${date.hijri.year}`,
    city,
    country,
    timezone: meta.timezone,
  };
}
