import type {
  AuthUser,
  HistoryPoint,
  VehicleAlertSummary,
  VehicleColorKey,
  VehicleKind,
  VehicleSummary,
} from "@tracker/shared";

const API_BASE_URL = "/api";

export type SessionPayload = {
  token: string;
  user: AuthUser;
};

export class ApiError extends Error {
  status: number;

  constructor(status: number, message: string) {
    super(message);
    this.name = "ApiError";
    this.status = status;
  }
}

export type AdminUserSummary = {
  id: string;
  email: string;
  firstName: string;
  lastName: string;
  role: "admin" | "user";
  canEditVehicleInspection: boolean;
  canViewStats: boolean;
  createdAt: string;
  vehicleIds: string[];
};

export type AdminDeviceSummary = {
  id: string;
  imei: string;
  protocol_name: string;
  phone_number: string | null;
  status: string;
  last_seen: string | null;
  vehicle_id: string | null;
};

export type CompanySettingsSummary = {
  addressInput: string;
  addressLabel: string;
  lat: number;
  lon: number;
  updatedAt: string;
} | null;

export type CompanyRouteSummary = {
  distanceKm: number;
  durationMinutes: number;
  companyAddress: string;
};

export type InspectionEmailSettingsSummary = {
  recipients: string[];
  updatedAt: string | null;
};

export type VehicleStatsRecord = {
  vehicleId: string;
  vehicleName: string;
  plateNumber: string | null;
  colorKey: VehicleColorKey | null;
  vehicleKind: VehicleKind;
  value: number;
  unit: "km/h" | "km";
  recordedAt: string | null;
  recordedDay?: string | null;
};

export type VehicleStatsOverview = {
  topSpeed: VehicleStatsRecord | null;
  topDistance: VehicleStatsRecord | null;
  topSpeeds: VehicleStatsRecord[];
  topDistances: VehicleStatsRecord[];
  topMonthlyDistance: VehicleStatsRecord | null;
  topYearlyDistance: VehicleStatsRecord | null;
  topMonthlyDistances: VehicleStatsRecord[];
  topYearlyDistances: VehicleStatsRecord[];
  selectedVehicleStats: {
    vehicleId: string;
    vehicleName: string;
    plateNumber: string | null;
    colorKey: VehicleColorKey | null;
    vehicleKind: VehicleKind;
    estimatedCurrentOdometer: number | null;
    currentMonthKm: number;
    currentYearKm: number;
    bestDayKm: number;
    bestDay: string | null;
    monthlyRank: number | null;
    yearlyRank: number | null;
  } | null;
};

export type VehicleTripSummary = {
  tripId: number;
  startTime: string;
  endTime: string;
  startLat: number;
  startLon: number;
  endLat: number;
  endLon: number;
  startLabel: string | null;
  endLabel: string | null;
  durationMinutes: number | null;
  distanceKm: number | null;
};

export const sessionStorageKey = "tracker.session";

export const loadSession = (): SessionPayload | null => {
  if (typeof window === "undefined") {
    return null;
  }

  const raw = window.localStorage.getItem(sessionStorageKey);
  if (!raw) {
    return null;
  }

  try {
    return JSON.parse(raw) as SessionPayload;
  } catch {
    return null;
  }
};

export const saveSession = (session: SessionPayload) => {
  window.localStorage.setItem(sessionStorageKey, JSON.stringify(session));
};

export const clearSession = () => {
  window.localStorage.removeItem(sessionStorageKey);
};

export const apiFetch = async <T>(path: string, token?: string, init?: RequestInit): Promise<T> => {
  const response = await fetch(`${API_BASE_URL}${path}`, {
    ...init,
    headers: {
      "Content-Type": "application/json",
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
      ...(init?.headers ?? {}),
    },
    cache: "no-store",
  });

  if (!response.ok) {
    const body = await response.json().catch(() => ({ message: "Request failed" }));
    throw new ApiError(response.status, body.message ?? "Request failed");
  }

  if (response.status === 204) {
    return undefined as T;
  }

  return response.json() as Promise<T>;
};

export const loginRequest = (email: string, password: string) =>
  apiFetch<{ token: string; user: AuthUser }>("/auth/login", undefined, {
    method: "POST",
    body: JSON.stringify({ email, password }),
  });

export const fetchVehicles = (token: string) =>
  apiFetch<{ items: VehicleSummary[] }>("/vehicles", token);

export const fetchVehicleStatsOverview = (token: string, vehicleId?: string | null) =>
  apiFetch<{ item: VehicleStatsOverview }>(
    `/vehicles/stats/overview${vehicleId ? `?vehicleId=${encodeURIComponent(vehicleId)}` : ""}`,
    token,
  );

export const fetchVehicleHistory = (token: string, vehicleId: string, from: string, to: string) =>
  apiFetch<{
    items: Array<{
      id: number;
      lat: number;
      lon: number;
      speed?: number | null;
      heading?: number | null;
      deviceTime?: string | null;
      serverTime?: string | null;
      ignition?: boolean | null;
      battery?: number | null;
      gsmSignal?: number | null;
      satelliteCount?: number | null;
      gpsValid?: boolean | null;
      charging?: boolean | null;
      defense?: boolean | null;
      positionType?: string | null;
      device_time?: string | null;
      server_time?: string | null;
      gsm_signal?: number | null;
      satellite_count?: number | null;
      gps_valid?: boolean | null;
      position_type?: string | null;
    }>;
  }>(
    `/vehicles/${vehicleId}/history?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}&limit=5000`,
    token,
  ).then((response) => ({
    items: response.items.map((item) => ({
      id: item.id,
      lat: item.lat,
      lon: item.lon,
      speed: item.speed ?? null,
      heading: item.heading ?? null,
      deviceTime: item.deviceTime ?? item.device_time ?? null,
      serverTime: item.serverTime ?? item.server_time ?? "",
      ignition: item.ignition ?? null,
      battery: item.battery ?? null,
      gsmSignal: item.gsmSignal ?? item.gsm_signal ?? null,
      satelliteCount: item.satelliteCount ?? item.satellite_count ?? null,
      gpsValid: item.gpsValid ?? item.gps_valid ?? null,
      charging: item.charging ?? null,
      defense: item.defense ?? null,
      positionType: item.positionType ?? item.position_type ?? null,
    })),
  }));

export const fetchVehiclePreviousPosition = (token: string, vehicleId: string, before: string) =>
  apiFetch<{
    item: {
      id: number;
      lat: number;
      lon: number;
      speed?: number | null;
      heading?: number | null;
      device_time?: string | null;
      server_time?: string | null;
      ignition?: boolean | null;
      battery?: number | null;
      gsm_signal?: number | null;
      satellite_count?: number | null;
      gps_valid?: boolean | null;
      charging?: boolean | null;
      defense?: boolean | null;
      position_type?: string | null;
    } | null;
  }>(
    `/vehicles/${vehicleId}/previous-position?before=${encodeURIComponent(before)}`,
    token,
  ).then((response) => ({
    item: response.item
      ? {
          id: response.item.id,
          lat: response.item.lat,
          lon: response.item.lon,
          speed: response.item.speed ?? null,
          heading: response.item.heading ?? null,
          deviceTime: response.item.device_time ?? null,
          serverTime: response.item.server_time ?? "",
          ignition: response.item.ignition ?? null,
          battery: response.item.battery ?? null,
          gsmSignal: response.item.gsm_signal ?? null,
          satelliteCount: response.item.satellite_count ?? null,
          gpsValid: response.item.gps_valid ?? null,
          charging: response.item.charging ?? null,
          defense: response.item.defense ?? null,
          positionType: response.item.position_type ?? null,
        }
      : null,
  }));

export const fetchVehicleTrips = (
  token: string,
  vehicleId: string,
  days = 14,
  limit = 20,
  before?: string,
) =>
  apiFetch<{ items: VehicleTripSummary[]; hasMore: boolean; nextBefore: string | null }>(
    `/vehicles/${vehicleId}/trips?days=${encodeURIComponent(String(days))}&limit=${encodeURIComponent(String(limit))}${before ? `&before=${encodeURIComponent(before)}` : ""}`,
    token,
  );

export const reverseGeocode = (token: string, lat: number, lon: number) =>
  apiFetch<{ item: { label: string } }>(
    `/geocoding/reverse?lat=${encodeURIComponent(String(lat))}&lon=${encodeURIComponent(String(lon))}`,
    token,
  );

export const fetchUnassignedDevices = (token: string) =>
  apiFetch<{
    items: Array<{
      imei: string;
      protocol_name: string;
      first_seen: string;
      last_seen: string;
      raw_payload: string;
    }>;
  }>("/devices/unassigned", token);

export const fetchUsers = (token: string) =>
  apiFetch<{ items: AdminUserSummary[] }>("/users", token);

export const fetchDevices = (token: string) =>
  apiFetch<{ items: AdminDeviceSummary[] }>("/devices", token);

export const fetchCompanySettings = (token: string) =>
  apiFetch<{ item: CompanySettingsSummary }>("/settings/company", token);

export const updateCompanySettings = (token: string, payload: { address: string }) =>
  apiFetch<{ item: CompanySettingsSummary }>("/settings/company", token, {
    method: "PATCH",
    body: JSON.stringify(payload),
  });

export const sendTestTrackingEmail = (token: string) =>
  apiFetch<{ item: { accepted: string[]; rejected: string[]; messageId: string } }>("/settings/email/test", token, {
    method: "POST",
  });

export const fetchInspectionEmailSettings = (token: string) =>
  apiFetch<{ item: InspectionEmailSettingsSummary }>("/settings/inspection-email", token);

export const updateInspectionEmailSettings = (token: string, payload: { recipients: string[] }) =>
  apiFetch<{ item: InspectionEmailSettingsSummary }>("/settings/inspection-email", token, {
    method: "PATCH",
    body: JSON.stringify(payload),
  });

export const fetchCompanyRoute = (token: string, lat: number, lon: number, signal?: AbortSignal) =>
  apiFetch<{ item: CompanyRouteSummary }>(
    `/settings/company-route?lat=${encodeURIComponent(String(lat))}&lon=${encodeURIComponent(String(lon))}`,
    token,
    signal ? { signal } : undefined,
  );

export const createUser = (
  token: string,
  payload: { email: string; firstName: string; lastName: string; password: string; role: "admin" | "user"; canEditVehicleInspection: boolean; canViewStats: boolean; vehicleIds: string[] },
) =>
  apiFetch<{ item: AdminUserSummary }>("/users", token, {
    method: "POST",
    body: JSON.stringify(payload),
  });

export const updateUser = (
  token: string,
  userId: string,
  payload: { firstName: string; lastName: string; password?: string; role: "admin" | "user"; canEditVehicleInspection: boolean; canViewStats: boolean; vehicleIds: string[] },
) =>
  apiFetch<{ item: AdminUserSummary }>(`/users/${userId}`, token, {
    method: "PATCH",
    body: JSON.stringify(payload),
  });

export const deleteUser = (token: string, userId: string) =>
  apiFetch<unknown>(`/users/${userId}`, token, {
    method: "DELETE",
  });

export const fetchActiveAlerts = (token: string) =>
  apiFetch<{ items: VehicleAlertSummary[] }>("/alerts/active", token);

export const fetchRecentAlerts = (token: string) =>
  apiFetch<{ items: VehicleAlertSummary[] }>("/alerts/recent", token);

export const acknowledgeAlert = (token: string, alertId: string) =>
  apiFetch<{ item: { id: string; acknowledgedAt: string } }>(`/alerts/${alertId}/acknowledge`, token, {
    method: "PATCH",
  });

export const createVehicle = (
  token: string,
  payload: { name: string; plateNumber?: string; colorKey: VehicleColorKey; vehicleKind: VehicleKind },
) =>
  apiFetch<{ item: { id: string; name: string; plateNumber: string | null; colorKey: VehicleColorKey; vehicleKind: VehicleKind } }>("/vehicles", token, {
    method: "POST",
    body: JSON.stringify({
      name: payload.name,
      plateNumber: payload.plateNumber,
      colorKey: payload.colorKey,
      vehicleKind: payload.vehicleKind,
      userIds: [],
    }),
  });

export const createDevice = (
  token: string,
  payload: { imei: string; phoneNumber?: string; vehicleId?: string },
) =>
  apiFetch<{ item: { id: string; imei: string; vehicle_id: string | null } }>("/devices", token, {
    method: "POST",
    body: JSON.stringify({
      imei: payload.imei,
      phoneNumber: payload.phoneNumber,
      protocolName: "gt06",
      vehicleId: payload.vehicleId,
    }),
  });

export const updateVehicle = (
  token: string,
  vehicleId: string,
  payload: {
    name: string;
    plateNumber?: string | null;
    colorKey: VehicleColorKey;
    vehicleKind: VehicleKind;
    inspectionDueDate?: string | null;
    oilChangeDueDate?: string | null;
    oilChangeOdometer?: number | null;
    oilChangeIntervalKm?: number | null;
    tireChangedAt?: string | null;
    nextTireChangeDueDate?: string | null;
  },
) =>
  apiFetch<{ item: { id: string; name: string; plateNumber: string | null; colorKey: VehicleColorKey; vehicleKind: VehicleKind } }>(
    `/vehicles/${vehicleId}`,
    token,
    {
      method: "PATCH",
      body: JSON.stringify({
        name: payload.name,
        plateNumber: payload.plateNumber ?? null,
        colorKey: payload.colorKey,
        vehicleKind: payload.vehicleKind,
        inspectionDueDate: payload.inspectionDueDate ?? null,
        oilChangeDueDate: payload.oilChangeDueDate ?? null,
        oilChangeOdometer: payload.oilChangeOdometer ?? null,
        oilChangeIntervalKm: payload.oilChangeIntervalKm ?? null,
        tireChangedAt: payload.tireChangedAt ?? null,
        nextTireChangeDueDate: payload.nextTireChangeDueDate ?? null,
      }),
    },
  );

export const updateVehicleInspection = (
  token: string,
  vehicleId: string,
  payload: {
    inspectionDueDate: string | null;
    oilChangeDueDate: string | null;
    oilChangeOdometer: number | null;
    oilChangeIntervalKm: number | null;
    tireChangedAt: string | null;
    nextTireChangeDueDate: string | null;
  },
) =>
  apiFetch<{ item: VehicleSummary }>(`/vehicles/${vehicleId}/inspection`, token, {
    method: "PATCH",
    body: JSON.stringify(payload),
  });

export const deleteVehicle = (token: string, vehicleId: string) =>
  apiFetch<unknown>(`/vehicles/${vehicleId}`, token, {
    method: "DELETE",
  });

export const updateDevice = (
  token: string,
  deviceId: string,
  payload: { imei: string; phoneNumber?: string | null; vehicleId?: string | null },
) =>
  apiFetch<{ item: { id: string; imei: string; phone_number: string | null; vehicle_id: string | null } }>(
    `/devices/${deviceId}`,
    token,
    {
      method: "PATCH",
      body: JSON.stringify({
        imei: payload.imei,
        phoneNumber: payload.phoneNumber ?? null,
        protocolName: "gt06",
        vehicleId: payload.vehicleId ?? null,
      }),
    },
  );

export const deleteDevice = (token: string, deviceId: string) =>
  apiFetch<unknown>(`/devices/${deviceId}`, token, {
    method: "DELETE",
  });
