export type MapSource = "mapy.cz" | "google-maps" | "coordinates" | "geocoded" | "unknown";

export interface MapCoordinates {
  latitude: number;
  longitude: number;
  zoom?: number;
  address?: string;
  source: MapSource;
  street?: string;
  houseNumber?: string;
  city?: string;
  zip?: string;
  country?: string;
}

export interface MapTileStyle {
  id: string;
  name: string;
  description: string;
  url: string;
  attribution: string;
  tileClassName?: string;
}

export const DEFAULT_MAP_STYLE_ID = "positron";

export const MAP_TILE_STYLES: readonly MapTileStyle[] = [
  {
    id: "positron",
    name: "Minimal",
    description: "Ultra-clean light basemap. Perfect for shadcn-style dashboards.",
    url: "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",
    attribution: "&copy; OpenStreetMap contributors &copy; CARTO",
  },
  {
    id: "bookra-voyager",
    name: "Bookra Voyager",
    description: "Warm, calm default that fits Bookra surfaces.",
    url: "https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png",
    attribution: "&copy; OpenStreetMap contributors &copy; CARTO",
    tileClassName: "bookra-map-tiles-warm",
  },
  {
    id: "light",
    name: "Clean Light",
    description: "Quiet light basemap with high readability.",
    url: "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",
    attribution: "&copy; OpenStreetMap contributors &copy; CARTO",
  },
  {
    id: "dark",
    name: "Dark Matter",
    description: "Dark basemap for dark sections and evening brands.",
    url: "https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png",
    attribution: "&copy; OpenStreetMap contributors &copy; CARTO",
  },
  {
    id: "voyager",
    name: "Voyager",
    description: "Balanced color and street detail.",
    url: "https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png",
    attribution: "&copy; OpenStreetMap contributors &copy; CARTO",
  },
  {
    id: "openstreetmap",
    name: "OpenStreetMap",
    description: "Standard OSM tiles.",
    url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
    attribution: "&copy; OpenStreetMap contributors",
  },
  {
    id: "satellite",
    name: "Satellite",
    description: "Esri satellite imagery.",
    url: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
    attribution: "Tiles &copy; Esri",
  },
];

export const MAP_STYLE_OPTIONS = [
  ...MAP_TILE_STYLES.map((style) => ({
    value: style.id,
    label: style.name,
  })),
  {
    value: "custom",
    label: "Custom tile URL",
  },
];

export function mapStyleById(styleId: string | undefined): MapTileStyle {
  return MAP_TILE_STYLES.find((style) => style.id === styleId) ?? MAP_TILE_STYLES[0];
}

export function resolveMapTileStyle(styleId: string | undefined, customTileUrl?: string): MapTileStyle {
  const trimmedUrl = customTileUrl?.trim();
  if (styleId === "custom" && trimmedUrl) {
    return {
      id: "custom",
      name: "Custom",
      description: "User-provided tile URL.",
      url: trimmedUrl,
      attribution: "&copy; OpenStreetMap contributors",
    };
  }
  return mapStyleById(styleId);
}

export function validateCoordinates(lat: number, lng: number): boolean {
  return (
    Number.isFinite(lat) &&
    Number.isFinite(lng) &&
    lat >= -90 &&
    lat <= 90 &&
    lng >= -180 &&
    lng <= 180
  );
}

export function parseCoordinateText(input: string): MapCoordinates | null {
  const match = input
    .trim()
    .match(/^\s*(-?\d+(?:\.\d+)?)\s*[,;\s]\s*(-?\d+(?:\.\d+)?)\s*(?:[,;\s]\s*(\d{1,2}))?\s*$/);
  if (!match) return null;

  const latitude = Number(match[1]);
  const longitude = Number(match[2]);
  const zoom = match[3] ? Number(match[3]) : undefined;

  if (!validateCoordinates(latitude, longitude)) return null;

  return {
    latitude,
    longitude,
    zoom: Number.isFinite(zoom) ? zoom : undefined,
    source: "coordinates",
  };
}

export function parseMapyCzUrl(url: string): MapCoordinates | null {
  try {
    const urlObj = new URL(url);
    if (!urlObj.hostname.includes("mapy.cz") && !urlObj.hostname.includes("mapy.com")) {
      return null;
    }

    const longitude = Number(urlObj.searchParams.get("x"));
    const latitude = Number(urlObj.searchParams.get("y"));
    const zoom = Number(urlObj.searchParams.get("z"));
    const address = urlObj.searchParams.get("q") ?? undefined;

    if (!validateCoordinates(latitude, longitude)) return null;

    return {
      latitude,
      longitude,
      zoom: Number.isFinite(zoom) ? zoom : undefined,
      address: address ? decodeURIComponent(address) : undefined,
      source: "mapy.cz",
    };
  } catch {
    return null;
  }
}

export function parseGoogleMapsUrl(url: string): MapCoordinates | null {
  try {
    const urlObj = new URL(url);
    if (
      !urlObj.hostname.includes("google.") &&
      !urlObj.hostname.includes("goo.gl") &&
      !urlObj.hostname.includes("maps.app.goo.gl")
    ) {
      return null;
    }

    const pathMatch = urlObj.href.match(/@(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?),(\d+)(?:[mz])/);
    if (pathMatch) {
      const latitude = Number(pathMatch[1]);
      const longitude = Number(pathMatch[2]);
      const zoom = Number(pathMatch[3]);
      if (validateCoordinates(latitude, longitude)) {
        const placeMatch = urlObj.pathname.match(/\/place\/([^/]+)/);
        return {
          latitude,
          longitude,
          zoom: Number.isFinite(zoom) ? zoom : undefined,
          address: placeMatch ? decodeURIComponent(placeMatch[1].replace(/\+/g, " ")) : undefined,
          source: "google-maps",
        };
      }
    }

    const qParam = urlObj.searchParams.get("q");
    if (qParam) {
      const coordinateResult = parseCoordinateText(qParam);
      if (coordinateResult) {
        return {
          ...coordinateResult,
          source: "google-maps",
        };
      }
    }

    const dataMatch = urlObj.href.match(/!3d(-?\d+(?:\.\d+)?)!4d(-?\d+(?:\.\d+)?)/);
    if (dataMatch) {
      const latitude = Number(dataMatch[1]);
      const longitude = Number(dataMatch[2]);
      if (validateCoordinates(latitude, longitude)) {
        return {
          latitude,
          longitude,
          source: "google-maps",
        };
      }
    }

    return null;
  } catch {
    return null;
  }
}

export function parseMapUrl(input: string): MapCoordinates | null {
  if (!input.trim()) return null;

  let normalizedUrl = input.trim();
  if (!normalizedUrl.startsWith("http://") && !normalizedUrl.startsWith("https://")) {
    normalizedUrl = `https://${normalizedUrl}`;
  }

  return parseMapyCzUrl(normalizedUrl) ?? parseGoogleMapsUrl(normalizedUrl);
}

export async function geocodeLocation(query: string, signal?: AbortSignal): Promise<MapCoordinates | null> {
  const trimmed = query.trim();
  if (!trimmed) return null;

  const response = await fetch(
    `https://nominatim.openstreetmap.org/search?format=jsonv2&limit=1&addressdetails=1&accept-language=cs,en&q=${encodeURIComponent(trimmed)}`,
    { signal },
  );

  if (!response.ok) {
    throw new Error("Location search failed");
  }

  const results = (await response.json()) as Array<{
    lat?: string;
    lon?: string;
    display_name?: string;
    address?: {
      road?: string;
      street?: string;
      pedestrian?: string;
      footway?: string;
      house_number?: string;
      city?: string;
      town?: string;
      village?: string;
      municipality?: string;
      postcode?: string;
      country?: string;
    };
  }>;

  const first = results[0];
  if (!first) return null;

  const latitude = Number(first.lat);
  const longitude = Number(first.lon);
  if (!validateCoordinates(latitude, longitude)) return null;

  const address = first.address ?? {};
  return {
    latitude,
    longitude,
    zoom: 16,
    address: first.display_name ?? trimmed,
    source: "geocoded",
    street: address.road ?? address.street ?? address.pedestrian ?? address.footway,
    houseNumber: address.house_number,
    city: address.city ?? address.town ?? address.village ?? address.municipality,
    zip: address.postcode,
    country: address.country,
  };
}

export async function reverseGeocode(lat: number, lng: number, signal?: AbortSignal): Promise<Partial<MapCoordinates>> {
  if (!validateCoordinates(lat, lng)) return {};

  const response = await fetch(
    `https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${lng}&addressdetails=1&accept-language=cs,en`,
    { signal },
  );

  if (!response.ok) return {};

  const data = (await response.json()) as {
    display_name?: string;
    address?: {
      road?: string;
      street?: string;
      pedestrian?: string;
      footway?: string;
      house_number?: string;
      city?: string;
      town?: string;
      village?: string;
      municipality?: string;
      postcode?: string;
      country?: string;
    };
  };
  const address = data.address ?? {};

  return {
    address: data.display_name,
    street: address.road ?? address.street ?? address.pedestrian ?? address.footway,
    houseNumber: address.house_number,
    city: address.city ?? address.town ?? address.village ?? address.municipality,
    zip: address.postcode,
    country: address.country,
  };
}

export async function resolveLocationInput(input: string, signal?: AbortSignal): Promise<MapCoordinates | null> {
  const coordinateResult = parseCoordinateText(input);
  if (coordinateResult) {
    const reverse = await reverseGeocode(coordinateResult.latitude, coordinateResult.longitude, signal).catch(() => ({}));
    return {
      ...coordinateResult,
      ...reverse,
    };
  }

  const urlResult = parseMapUrl(input);
  if (urlResult) {
    const reverse = urlResult.address
      ? {}
      : await reverseGeocode(urlResult.latitude, urlResult.longitude, signal).catch(() => ({}));
    return {
      ...urlResult,
      ...reverse,
    };
  }

  return geocodeLocation(input, signal);
}
