import type { ReactNode } from 'react';
import { EnvironmentOutlined, ExportOutlined } from '@ant-design/icons';
import { Button, Space, Tag, Typography } from 'antd';
import './EmbeddedMapUI.css';

const { Paragraph, Text, Title } = Typography;

export type EmbeddedMapUIProps = {
  latitude: number;
  longitude: number;
  zoom?: number;
  height?: number;
  title?: string;
  address?: string;
  description?: string;
  markerLabel?: string;
  className?: string;
  radiusMeters?: number;
  lockViewport?: boolean;
  secondaryMarker?: {
    latitude: number;
    longitude: number;
    label?: string;
  } | null;
  overlay?: ReactNode;
};

type Bounds = {
  minLatitude: number;
  maxLatitude: number;
  minLongitude: number;
  maxLongitude: number;
};

function clampZoom(zoom: number) {
  return Math.min(18, Math.max(8, zoom));
}

function metersToLatitudeDegrees(meters: number) {
  return meters / 111320;
}

function metersToLongitudeDegrees(meters: number, latitude: number) {
  const safeLatitude = Math.max(-89.9999, Math.min(89.9999, latitude));
  const denominator = 111320 * Math.cos((safeLatitude * Math.PI) / 180);
  return meters / Math.max(denominator, 0.000001);
}

function createViewUrl(latitude: number, longitude: number, zoom: number) {
  const params = new URLSearchParams({
    mlon: String(longitude),
    mlat: String(latitude),
  });

  return `https://www.openstreetmap.org/?${params.toString()}#map=${clampZoom(zoom)}/${latitude}/${longitude}`;
}

function createBounds(
  latitude: number,
  longitude: number,
  radiusMeters: number | undefined,
  secondaryMarker: EmbeddedMapUIProps['secondaryMarker'],
): Bounds {
  const coverageMeters = Math.max(radiusMeters ?? 0, secondaryMarker ? 180 : 120, 120) * 1.8;
  const latitudePadding = metersToLatitudeDegrees(coverageMeters);
  const longitudePadding = metersToLongitudeDegrees(coverageMeters, latitude);

  let minLatitude = latitude - latitudePadding;
  let maxLatitude = latitude + latitudePadding;
  let minLongitude = longitude - longitudePadding;
  let maxLongitude = longitude + longitudePadding;

  if (secondaryMarker) {
    const extraLatitudePadding = metersToLatitudeDegrees(coverageMeters * 0.35);
    const extraLongitudePadding = metersToLongitudeDegrees(
      coverageMeters * 0.35,
      (latitude + secondaryMarker.latitude) / 2,
    );

    minLatitude = Math.min(minLatitude, secondaryMarker.latitude - extraLatitudePadding);
    maxLatitude = Math.max(maxLatitude, secondaryMarker.latitude + extraLatitudePadding);
    minLongitude = Math.min(minLongitude, secondaryMarker.longitude - extraLongitudePadding);
    maxLongitude = Math.max(maxLongitude, secondaryMarker.longitude + extraLongitudePadding);
  }

  return {
    minLatitude,
    maxLatitude,
    minLongitude,
    maxLongitude,
  };
}

function createEmbedUrl(bounds: Bounds, latitude: number, longitude: number) {
  const params = new URLSearchParams({
    bbox: [
      bounds.minLongitude,
      bounds.minLatitude,
      bounds.maxLongitude,
      bounds.maxLatitude,
    ].join(','),
    layer: 'mapnik',
    marker: `${latitude},${longitude}`,
  });

  return `https://www.openstreetmap.org/export/embed.html?${params.toString()}`;
}

function projectPoint(latitude: number, longitude: number, bounds: Bounds) {
  const xRatio =
    (longitude - bounds.minLongitude) / Math.max(bounds.maxLongitude - bounds.minLongitude, 0.000001);
  const yRatio =
    (bounds.maxLatitude - latitude) / Math.max(bounds.maxLatitude - bounds.minLatitude, 0.000001);

  return {
    x: Math.min(1, Math.max(0, xRatio)) * 100,
    y: Math.min(1, Math.max(0, yRatio)) * 100,
  };
}

function calculateCircleSizePercent(radiusMeters: number, bounds: Bounds, latitude: number) {
  const diameterLongitudeDegrees = metersToLongitudeDegrees(radiusMeters * 2, latitude);
  return (diameterLongitudeDegrees / Math.max(bounds.maxLongitude - bounds.minLongitude, 0.000001)) * 100;
}

export function EmbeddedMapUI({
  latitude,
  longitude,
  zoom = 15,
  height = 360,
  title = '현장 위치',
  address,
  description = '모바일과 데스크톱에서 바로 확인할 수 있는 내장 지도입니다.',
  markerLabel = '현장',
  className,
  radiusMeters,
  lockViewport = false,
  secondaryMarker,
  overlay,
}: EmbeddedMapUIProps) {
  const resolvedZoom = clampZoom(zoom);
  const viewUrl = createViewUrl(latitude, longitude, resolvedZoom);
  const bounds = createBounds(latitude, longitude, radiusMeters, secondaryMarker);
  const embedUrl = createEmbedUrl(bounds, latitude, longitude);
  const primaryPoint = projectPoint(latitude, longitude, bounds);
  const secondaryPoint = secondaryMarker
    ? projectPoint(secondaryMarker.latitude, secondaryMarker.longitude, bounds)
    : null;
  const radiusPercent =
    radiusMeters && radiusMeters > 0 ? calculateCircleSizePercent(radiusMeters, bounds, latitude) : null;

  return (
    <div className={['embedded-map-ui', className].filter(Boolean).join(' ')}>
      <div className="embedded-map-ui__header">
        <div className="embedded-map-ui__copy">
          <Space size={8} wrap>
            <Tag color="cyan" icon={<EnvironmentOutlined />}>
              {markerLabel}
            </Tag>
            <Text type="secondary">{`${latitude.toFixed(5)}, ${longitude.toFixed(5)}`}</Text>
          </Space>
          <Title level={4}>{title}</Title>
          <Paragraph>{description}</Paragraph>
          {address ? <Text type="secondary">{address}</Text> : null}
        </div>
        <Button href={viewUrl} target="_blank" rel="noreferrer" icon={<ExportOutlined />}>
          외부 지도로 열기
        </Button>
      </div>

      <div className="embedded-map-ui__frame" style={{ height }}>
        <iframe
          className={[
            'embedded-map-ui__canvas',
            lockViewport ? 'embedded-map-ui__canvas--locked' : null,
          ]
            .filter(Boolean)
            .join(' ')}
          src={embedUrl}
          title={title}
          loading="lazy"
          referrerPolicy="no-referrer-when-downgrade"
        />
        <div className="embedded-map-ui__overlay">
          <svg
            className="embedded-map-ui__overlay-svg"
            viewBox="0 0 100 100"
            preserveAspectRatio="none"
            aria-hidden="true"
          >
            {radiusPercent ? (
              <circle
                className="embedded-map-ui__radius"
                cx={primaryPoint.x}
                cy={primaryPoint.y}
                r={Math.max(2, radiusPercent / 2)}
              />
            ) : null}
            {secondaryPoint ? (
              <>
                <line
                  className="embedded-map-ui__link"
                  x1={primaryPoint.x}
                  y1={primaryPoint.y}
                  x2={secondaryPoint.x}
                  y2={secondaryPoint.y}
                />
                <circle className="embedded-map-ui__marker embedded-map-ui__marker--secondary" cx={secondaryPoint.x} cy={secondaryPoint.y} r="1.8" />
              </>
            ) : null}
            <circle className="embedded-map-ui__marker embedded-map-ui__marker--primary" cx={primaryPoint.x} cy={primaryPoint.y} r="2.2" />
          </svg>
          <div
            className="embedded-map-ui__label embedded-map-ui__label--primary"
            style={{ left: `${primaryPoint.x}%`, top: `${primaryPoint.y}%` }}
          >
            {markerLabel}
          </div>
          {secondaryPoint ? (
            <div
              className="embedded-map-ui__label embedded-map-ui__label--secondary"
              style={{ left: `${secondaryPoint.x}%`, top: `${secondaryPoint.y}%` }}
            >
              {secondaryMarker?.label ?? '보조 위치'}
            </div>
          ) : null}
          {lockViewport ? (
            <div className="embedded-map-ui__lock-badge">반경 오버레이 고정</div>
          ) : null}
          {overlay ? <div className="embedded-map-ui__slot">{overlay}</div> : null}
        </div>
      </div>

      <div className="embedded-map-ui__footer">
        <div className="embedded-map-ui__meta">
          <Tag bordered={false}>Zoom {resolvedZoom}</Tag>
          <Tag bordered={false}>Radius {radiusMeters ? `${Math.round(radiusMeters)}m` : 'Off'}</Tag>
          <Tag bordered={false}>{lockViewport ? 'Viewport Locked' : 'Viewport Free'}</Tag>
          <Tag bordered={false}>Embed Map</Tag>
        </div>
        <Text type="secondary">
          OpenStreetMap 임베드와 좌표 오버레이로 앱 내부에서 위치를 확인합니다. 반경 오버레이 사용 시 지도 이동은 잠금 처리합니다.
        </Text>
      </div>
    </div>
  );
}
