import { NextRequest, NextResponse } from 'next/server';
import type { RouteSegment, C3NavRouteRequest } from '@/types';

// OSRM public server for outdoor routing
const OSRM_API = 'https://router.project-osrm.org';

// c3nav routing API
const C3NAV_EVENTS = ['39c3', '38c3', '37c3', 'eh22', 'eh2025', 'camp2023'];

interface RouteRequest {
  origin: {
    latitude: number;
    longitude: number;
    indoor?: { level: number; x: number; y: number };
  };
  destination: {
    latitude: number;
    longitude: number;
    indoor?: { level: number; x: number; y: number };
  };
  mode?: 'walking' | 'driving';
  eventId?: string; // for c3nav indoor routing
  options?: {
    avoidStairs?: boolean;
    wheelchair?: boolean;
  };
}

interface RouteResponse {
  success: boolean;
  route?: {
    segments: RouteSegment[];
    totalDistance: number;
    estimatedTime: number;
    summary: string;
  };
  error?: string;
}

// Fetch outdoor route from OSRM
async function getOutdoorRoute(
  origin: { latitude: number; longitude: number },
  destination: { latitude: number; longitude: number },
  mode: 'walking' | 'driving' = 'walking'
): Promise<RouteSegment | null> {
  const profile = mode === 'walking' ? 'foot' : 'driving';

  // OSRM uses [lng, lat] format
  const coords = `${origin.longitude},${origin.latitude};${destination.longitude},${destination.latitude}`;
  const url = `${OSRM_API}/route/v1/${profile}/${coords}?overview=full&geometries=geojson&steps=true`;

  try {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 10000);

    const response = await fetch(url, {
      headers: { 'User-Agent': 'rMaps.online/1.0' },
      signal: controller.signal,
    });

    clearTimeout(timeoutId);

    if (!response.ok) {
      console.error('OSRM error:', response.status);
      return null;
    }

    const data = await response.json();

    if (data.code !== 'Ok' || !data.routes?.[0]) {
      console.error('OSRM no route:', data.code);
      return null;
    }

    const route = data.routes[0];
    const geometry = route.geometry;

    // Build instructions from steps
    const instructions = route.legs?.[0]?.steps
      ?.map((step: { maneuver?: { instruction?: string } }) => step.maneuver?.instruction)
      .filter(Boolean)
      .join(' -> ');

    return {
      type: 'outdoor',
      coordinates: geometry.coordinates, // Already in [lng, lat] format
      distance: route.distance,
      duration: route.duration,
      instructions: instructions || 'Follow the route',
    };
  } catch (error) {
    console.error('OSRM fetch error:', error);
    return null;
  }
}

// Fetch indoor route from c3nav
async function getIndoorRoute(
  origin: { level: number; x: number; y: number },
  destination: { level: number; x: number; y: number },
  eventId: string,
  options?: { avoidStairs?: boolean; wheelchair?: boolean }
): Promise<RouteSegment[] | null> {
  if (!C3NAV_EVENTS.includes(eventId)) {
    console.error('Invalid c3nav event:', eventId);
    return null;
  }

  const apiUrl = `https://${eventId}.c3nav.de/api/v2/routing/route/`;

  const request: C3NavRouteRequest = {
    origin: {
      coordinates: [origin.x, origin.y, origin.level],
    },
    destination: {
      coordinates: [destination.x, destination.y, destination.level],
    },
    options: {
      mode: 'fastest',
      avoid_stairs: options?.avoidStairs,
      wheelchair: options?.wheelchair,
    },
  };

  try {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 10000);

    const response = await fetch(apiUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': 'anonymous',
        'User-Agent': 'rMaps.online/1.0',
      },
      body: JSON.stringify(request),
      signal: controller.signal,
    });

    clearTimeout(timeoutId);

    if (!response.ok) {
      console.error('c3nav routing error:', response.status);
      return null;
    }

    const data = await response.json();

    if (data.status !== 'ok' || !data.path) {
      console.error('c3nav no route:', data.status);
      return null;
    }

    // Group path points by level into segments
    const segments: RouteSegment[] = [];
    let currentLevel: number | null = null;
    let currentCoords: Array<[number, number]> = [];

    for (const point of data.path) {
      const level = point.level ?? point.coordinates[2];

      if (currentLevel !== null && level !== currentLevel) {
        // Level change - save current segment and start new one
        if (currentCoords.length > 0) {
          segments.push({
            type: 'indoor',
            coordinates: currentCoords,
            distance: 0, // Will be calculated
            duration: 0,
            level: currentLevel,
          });
        }
        // Add transition segment
        segments.push({
          type: 'transition',
          coordinates: [currentCoords[currentCoords.length - 1], [point.coordinates[0], point.coordinates[1]]],
          distance: 0,
          duration: 10, // Estimate 10s for level change
          instructions: `Go to level ${level}`,
          level,
        });
        currentCoords = [];
      }

      currentLevel = level;
      currentCoords.push([point.coordinates[0], point.coordinates[1]]);
    }

    // Add final segment
    if (currentCoords.length > 0 && currentLevel !== null) {
      segments.push({
        type: 'indoor',
        coordinates: currentCoords,
        distance: 0,
        duration: 0,
        level: currentLevel,
      });
    }

    // Calculate distances for each segment
    for (const segment of segments) {
      if (segment.coordinates.length > 1) {
        let dist = 0;
        for (let i = 1; i < segment.coordinates.length; i++) {
          const [x1, y1] = segment.coordinates[i - 1];
          const [x2, y2] = segment.coordinates[i];
          dist += Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
        }
        segment.distance = dist;
        // Estimate walking speed: ~1.4 m/s
        segment.duration = dist / 1.4;
      }
    }

    return segments;
  } catch (error) {
    console.error('c3nav fetch error:', error);
    return null;
  }
}

export async function POST(request: NextRequest) {
  try {
    const body: RouteRequest = await request.json();
    const { origin, destination, mode = 'walking', eventId = '39c3', options } = body;

    if (!origin || !destination) {
      return NextResponse.json(
        { success: false, error: 'Origin and destination required' },
        { status: 400 }
      );
    }

    const segments: RouteSegment[] = [];
    let totalDistance = 0;
    let estimatedTime = 0;
    let summary = '';

    // Determine routing mode based on indoor positions
    const isOriginIndoor = !!origin.indoor;
    const isDestIndoor = !!destination.indoor;

    if (isOriginIndoor && isDestIndoor) {
      // Both indoor - use c3nav
      const indoorSegments = await getIndoorRoute(
        origin.indoor!,
        destination.indoor!,
        eventId,
        options
      );

      if (indoorSegments) {
        segments.push(...indoorSegments);
        summary = 'Indoor route';
      } else {
        return NextResponse.json(
          { success: false, error: 'Could not calculate indoor route' },
          { status: 400 }
        );
      }
    } else if (!isOriginIndoor && !isDestIndoor) {
      // Both outdoor - use OSRM
      const outdoorSegment = await getOutdoorRoute(origin, destination, mode);

      if (outdoorSegment) {
        segments.push(outdoorSegment);
        summary = 'Outdoor route';
      } else {
        return NextResponse.json(
          { success: false, error: 'Could not calculate outdoor route' },
          { status: 400 }
        );
      }
    } else {
      // Mixed indoor/outdoor - need both
      if (isOriginIndoor && !isDestIndoor) {
        // Indoor to outdoor: indoor segment + outdoor segment
        // For now, use outdoor coordinates as exit point
        const outdoorSegment = await getOutdoorRoute(
          { latitude: origin.latitude, longitude: origin.longitude },
          destination,
          mode
        );

        if (outdoorSegment) {
          segments.push({
            type: 'transition',
            coordinates: [[origin.longitude, origin.latitude]],
            distance: 0,
            duration: 30, // Estimate 30s to exit building
            instructions: 'Exit the building',
          });
          segments.push(outdoorSegment);
          summary = 'Exit building, then outdoor route';
        }
      } else {
        // Outdoor to indoor: outdoor segment + indoor segment
        const outdoorSegment = await getOutdoorRoute(
          origin,
          { latitude: destination.latitude, longitude: destination.longitude },
          mode
        );

        if (outdoorSegment) {
          segments.push(outdoorSegment);
          segments.push({
            type: 'transition',
            coordinates: [[destination.longitude, destination.latitude]],
            distance: 0,
            duration: 30, // Estimate 30s to enter building
            instructions: 'Enter the building',
          });
          summary = 'Outdoor route, then enter building';
        }
      }

      if (segments.length === 0) {
        return NextResponse.json(
          { success: false, error: 'Could not calculate route' },
          { status: 400 }
        );
      }
    }

    // Calculate totals
    for (const seg of segments) {
      totalDistance += seg.distance;
      estimatedTime += seg.duration;
    }

    const response: RouteResponse = {
      success: true,
      route: {
        segments,
        totalDistance,
        estimatedTime,
        summary,
      },
    };

    return NextResponse.json(response, {
      status: 200,
      headers: {
        'Cache-Control': 'no-store',
        'Access-Control-Allow-Origin': '*',
      },
    });
  } catch (error) {
    console.error('Routing API error:', error);
    return NextResponse.json(
      { success: false, error: 'Internal server error' },
      { status: 500 }
    );
  }
}

export async function OPTIONS() {
  return new NextResponse(null, {
    status: 200,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'POST, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type',
    },
  });
}
