import { useCallback, useEffect, useMemo, useState } from "react";
import { getCargoUsed, refineVeldspar } from "./sliceEconomy";
import { getActionBlockedReason, getContextualSliceActions, getSliceFacts, type SliceActionId } from "./sliceController";
import { useGameSliceSession } from "./useGameSliceSession";
import { isBeltPoi, isStationPoi, SLICE_DURATIONS } from "./sliceWorld";
import type { SystemPointOfInterest, Vec3 } from "../r3f/shared/types";
import type { LocalWaypoint } from "../r3f/movement/movementState";
import type { GameSliceSession, SliceEvent, SliceFittedModule, SliceService, SliceTarget, SliceTargetKind } from "./types";

function completionEventFor(session: GameSliceSession): SliceEvent | null {
  const operation = session.activeOperation;
  if (!operation) return null;
  if (Date.now() - operation.startedAt < operation.durationMs) return null;

  if (operation.kind === "undocking") return { type: "station.undocked", systemId: session.currentSystemId };
  if (operation.kind === "travel") return { type: "navigation.arrived", systemId: operation.targetSystemId, poiId: operation.target.poiId ?? "", poiName: operation.targetPoiName };
  if (operation.kind === "docking") return { type: "station.docked", systemId: session.currentSystemId, stationPoiId: operation.stationPoiId, stationName: operation.stationName };
  if (operation.kind === "mining") return { type: "mining.completed", ore: operation.ore, quantity: operation.quantity };
  return null;
}

export function getOperationProgress(session: GameSliceSession, now: number): number {
  const operation = session.activeOperation;
  if (!operation) return 0;
  return Math.max(0, Math.min(1, (now - operation.startedAt) / operation.durationMs));
}

export function useSliceController() {
  const { session, emit, reset, missingRequestedSession } = useGameSliceSession({ createIfMissing: true });
  const [now, setNow] = useState(() => Date.now());

  useEffect(() => {
    const id = window.setInterval(() => setNow(Date.now()), 100);
    return () => window.clearInterval(id);
  }, []);

  useEffect(() => {
    if (!session?.activeOperation) return;
    const completeOperation = (current: GameSliceSession, completion: SliceEvent) => {
      const operation = current.activeOperation;
      emit(completion);
      if (completion.type === "mining.completed") {
        emit({ type: "xp.awarded", skill: "Mining", amount: 25 });
        if (operation?.kind === "mining" && operation.repeat) {
          const freeAfterCycle = Math.max(0, current.cargoCapacity - getCargoUsed(current) - operation.quantity);
          if (freeAfterCycle > 0) {
            emit({
              type: "mining.started",
              ore: operation.ore,
              quantity: Math.min(1000, freeAfterCycle),
              targetPoiId: operation.targetPoiId,
              targetName: operation.targetName,
              repeat: true,
              startedAt: Date.now(),
              durationMs: SLICE_DURATIONS.mining,
            });
          }
        }
      }
    };
    const completion = completionEventFor(session);
    if (completion) {
      completeOperation(session, completion);
      return;
    }
    const remaining = Math.max(0, session.activeOperation.startedAt + session.activeOperation.durationMs - Date.now());
    const timeout = window.setTimeout(() => {
      const nextCompletion = completionEventFor(session);
      if (nextCompletion) {
        completeOperation(session, nextCompletion);
      }
    }, remaining + 20);
    return () => window.clearTimeout(timeout);
  }, [emit, session]);

  const facts = useMemo(() => session ? getSliceFacts(session) : null, [session]);
  const contextualActions = useMemo(() => session ? getContextualSliceActions(session) : null, [session]);
  const operationProgress = useMemo(() => session ? getOperationProgress(session, now) : 0, [now, session]);

  const blockedReason = useCallback((actionId: SliceActionId) => session ? getActionBlockedReason(session, actionId) : "Session is not ready.", [session]);

  const guarded = useCallback((actionId: SliceActionId, run: (session: GameSliceSession) => void) => {
    if (!session || getActionBlockedReason(session, actionId)) return;
    run(session);
  }, [session]);

  const startUndock = useCallback(() => guarded("undock", (current) => {
    emit({ type: "station.undockStarted", systemId: current.currentSystemId, startedAt: Date.now(), durationMs: SLICE_DURATIONS.undock });
  }), [emit, guarded]);

  const approachSelectedTarget = useCallback(() => guarded("approachTarget", (current) => {
    if (!current.selectedTarget) return;
    emit({ type: "navigation.approachStarted", target: current.selectedTarget, fromPosition: current.shipPosition, startedAt: Date.now(), durationMs: SLICE_DURATIONS.localTravel });
  }), [emit, guarded]);

  const approachTarget = useCallback((target: SliceTarget) => {
    if (!session) return;
    if (!session.dockedStationPoiId && !session.activeOperation && (!target.poiId || session.currentPoiId !== target.poiId)) {
      emit({ type: "navigation.targetSelected", target });
      emit({ type: "navigation.approachStarted", target, fromPosition: session.shipPosition, startedAt: Date.now(), durationMs: SLICE_DURATIONS.localTravel });
    } else {
      emit({ type: "navigation.targetSelected", target });
    }
  }, [emit, session]);

  const dock = useCallback(() => guarded("dock", (current) => {
    const target = current.selectedTarget?.poiId === current.currentPoiId && current.selectedTarget.kind === "station"
      ? current.selectedTarget
      : current.currentPoiId && isStationPoi(current.currentPoiId)
        ? { id: current.currentPoiId, name: current.selectedTarget?.name ?? current.currentPoiId, kind: "station" as const, systemId: current.currentSystemId, poiId: current.currentPoiId, position: current.shipPosition }
        : null;
    if (!target?.poiId) return;
    emit({ type: "station.dockStarted", stationPoiId: target.poiId, stationName: target.name, startedAt: Date.now(), durationMs: SLICE_DURATIONS.docking });
  }), [emit, guarded]);

  const startMining = useCallback(({ repeat = true }: { repeat?: boolean } = {}) => guarded("startMining", (current) => {
    const facts = getSliceFacts(current);
    const target = current.selectedTarget?.poiId === current.currentPoiId && current.selectedTarget.kind === "asteroid_belt"
      ? current.selectedTarget
      : current.currentPoiId && isBeltPoi(current.currentPoiId)
        ? { id: current.currentPoiId, name: current.selectedTarget?.name ?? current.currentPoiId, kind: "asteroid_belt" as const, systemId: current.currentSystemId, poiId: current.currentPoiId, position: current.shipPosition }
        : null;
    if (!target?.poiId) return;
    emit({
      type: "mining.started",
      ore: "Veldspar",
      quantity: Math.min(1000, facts.freeCargo),
      targetPoiId: target.poiId,
      targetName: target.name,
      repeat,
      startedAt: Date.now(),
      durationMs: SLICE_DURATIONS.mining,
    });
  }), [emit, guarded]);

  const mineTarget = useCallback((target: SliceTarget) => {
    if (!session || session.activeOperation || session.dockedStationPoiId || target.kind !== "asteroid_belt" || session.currentPoiId !== target.poiId || !target.poiId) return;
    const facts = getSliceFacts(session);
    if (facts.freeCargo <= 0) return;
    emit({ type: "navigation.targetSelected", target });
    emit({
      type: "mining.started",
      ore: "Veldspar",
      quantity: Math.min(1000, facts.freeCargo),
      targetPoiId: target.poiId,
      targetName: target.name,
      repeat: true,
      startedAt: Date.now(),
      durationMs: SLICE_DURATIONS.mining,
    });
  }, [emit, session]);

  const dockAtTarget = useCallback((target: SliceTarget) => {
    if (!session || session.activeOperation || session.dockedStationPoiId || target.kind !== "station" || session.currentPoiId !== target.poiId || !target.poiId) return;
    emit({ type: "navigation.targetSelected", target });
    emit({ type: "station.dockStarted", stationPoiId: target.poiId, stationName: target.name, startedAt: Date.now(), durationMs: SLICE_DURATIONS.docking });
  }, [emit, session]);

  const stopMining = useCallback(() => {
    if (session?.activeOperation?.kind === "mining") emit({ type: "mining.stopped" });
  }, [emit, session?.activeOperation]);

  const openService = useCallback((service: SliceService) => {
    const commandByService: Record<SliceService, SliceActionId> = { refining: "openRefining", fitting: "openFitting", market: "openMarket" };
    guarded(commandByService[service], () => emit({ type: "station.serviceOpened", service }));
  }, [emit, guarded]);

  const closeService = useCallback(() => {
    if (session?.activeService) emit({ type: "station.serviceClosed" });
  }, [emit, session?.activeService]);

  const startCombat = useCallback(() => {
    if (!session || getActionBlockedReason(session, "startCombat")) return;
    emit({ type: "combat.started" });
    emit({ type: "zora.observed", trigger: "combat.started" });
  }, [emit, session]);

  const selectTarget = useCallback((target: SliceTarget, manual = false) => {
    emit({ type: manual ? "navigation.manualWaypointSelected" : "navigation.targetSelected", target });
  }, [emit]);

  const selectSystemPoiTarget = useCallback((systemId: string, poi: SystemPointOfInterest, position: Vec3) => {
    const kind: SliceTargetKind = poi.type === "station" ? "station" : poi.type === "asteroid_belt" ? "asteroid_belt" : "manual";
    selectTarget({
      id: poi.id,
      name: poi.name,
      kind,
      systemId,
      poiId: poi.id,
      position,
    });
  }, [selectTarget]);

  const selectPoiTarget = useCallback((poi: SystemPointOfInterest, position: Vec3) => {
    selectSystemPoiTarget(session?.currentSystemId ?? "sol", poi, position);
  }, [selectSystemPoiTarget, session?.currentSystemId]);

  const selectManualTarget = useCallback((waypoint: LocalWaypoint) => {
    selectTarget({
      id: waypoint.id,
      name: waypoint.name,
      kind: waypoint.type === "entity" && waypoint.id.includes("hostile") ? "hostile" : "manual",
      systemId: session?.currentSystemId ?? "sol",
      position: waypoint.position,
    }, true);
  }, [selectTarget, session?.currentSystemId]);

  const refineVeldsparStack = useCallback((inputQuantity: number) => {
    if (!session) return;
    const ore = session.cargo.find((item) => item.item === "Veldspar");
    const quantity = Math.min(inputQuantity, ore?.quantity ?? 0);
    if (quantity < 333) return;
    const used = Math.floor(quantity / 333) * 333;
    emit({ type: "refining.completed", ore: "Veldspar", inputQuantity: used, minerals: refineVeldspar(used) });
    emit({ type: "xp.awarded", skill: "Industry", amount: 35 });
  }, [emit, session]);

  const updateFitting = useCallback((modules: SliceFittedModule[]) => {
    emit({ type: "fitting.changed", modules });
  }, [emit]);

  const sellStack = useCallback((item: string, quantity: number, unitPrice: number) => {
    if (!session?.dockedStationPoiId || quantity <= 0) return;
    emit({ type: "market.sold", item, quantity, isk: quantity * unitPrice });
    emit({ type: "xp.awarded", skill: "Trade", amount: 30 });
    emit({ type: "zora.observed", trigger: "market.sold" });
  }, [emit, session?.dockedStationPoiId]);

  return {
    session,
    emit,
    reset,
    missingRequestedSession,
    facts,
    contextualActions,
    operationProgress,
    blockedReason,
    startUndock,
    selectTarget,
    selectPoiTarget,
    selectSystemPoiTarget,
    selectManualTarget,
    approachSelectedTarget,
    approachTarget,
    dock,
    dockAtTarget,
    startMining,
    mineTarget,
    stopMining,
    openService,
    closeService,
    startCombat,
    refineVeldsparStack,
    updateFitting,
    sellStack,
  };
}
