/**
 * Fitbit Companion App — Hevy Live Workout Bridge
 *
 * Runs inside the Fitbit phone app. Bridges:
 *   Watch (peerSocket) ↔ Pipo Backend (fetch)
 *
 * Responsibilities:
 *  - Fetch workout plan from backend on startup
 *  - Relay watch events to backend
 *  - Relay backend confirmations to watch
 *  - Cache plan + session for offline resilience
 */

import { peerSocket } from 'messaging';
import { me as device } from 'device';
import { settingsStorage } from 'settings';

// ─── Configuration ───────────────────────────────────────────────────

// In production, this comes from Fitbit settings or companion config.
// For the spike, hardcode with placeholder.
const BACKEND_BASE_URL = 'https://miopenclaw-vnic.tail9799d2.ts.net';
const USER_ID = 'chicho'; // from Fitbit settings

// ─── Types (mirror device state model) ───────────────────────────────

interface CompanionState {
  connected: boolean;
  backendReachable: boolean;
  pendingEvents: WatchEvent[];
  lastPlan: WorkoutPlanFromBackend | null;
}

interface WatchEvent {
  id: string;
  type: string;
  payload: Record<string, unknown>;
  timestamp: number;
}

interface WorkoutPlanFromBackend {
  routineId: string;
  routineName: string;
  exercises: Array<{
    templateId: string;
    name: string;
    type: string;
    sets: Array<{
      index: number;
      targetWeightKg: number | null;
      targetReps: string;
      targetRir: number | null;
    }>;
    notes: string | null;
  }>;
  fetchedAt: string;
}

// ─── State ───────────────────────────────────────────────────────────

const state: CompanionState = {
  connected: false,
  backendReachable: false,
  pendingEvents: [],
  lastPlan: null,
};

// ─── Backend HTTP helpers ────────────────────────────────────────────

async function fetchWorkoutPlan(): Promise<WorkoutPlanFromBackend | null> {
  try {
    const res = await fetch(`${BACKEND_BASE_URL}/api/workout-plan/${USER_ID}`);
    if (!res.ok) {
      console.error(`[companion] Plan fetch failed: ${res.status}`);
      return null;
    }
    const json = await res.json() as { ok: boolean; data: WorkoutPlanFromBackend };
    if (!json.ok) return null;
    return json.data;
  } catch (err) {
    console.error(`[companion] Plan fetch error: ${err}`);
    return null;
  }
}

async function postEvents(events: WatchEvent[]): Promise<boolean> {
  try {
    const res = await fetch(`${BACKEND_BASE_URL}/api/live-workout/events`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ userId: USER_ID, events }),
    });
    if (!res.ok) {
      console.error(`[companion] Event POST failed: ${res.status}`);
      return false;
    }
    const json = await res.json() as { ok: boolean };
    return json.ok ?? false;
  } catch (err) {
    console.error(`[companion] Event POST error: ${err}`);
    return false;
  }
}

async function completeWorkout(startedAt: string, endedAt: string): Promise<string | null> {
  try {
    const routineId = state.lastPlan?.routineId ?? 'unknown';
    const res = await fetch(`${BACKEND_BASE_URL}/api/workout/complete`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ userId: USER_ID, routineId, startedAt, endedAt }),
    });
    if (!res.ok) return null;
    const json = await res.json() as { ok: boolean; hevyWorkoutId: string };
    return json.ok ? json.hevyWorkoutId : null;
  } catch (err) {
    console.error(`[companion] Complete POST error: ${err}`);
    return null;
  }
}

// ─── Messaging: Watch → Companion ────────────────────────────────────

peerSocket.addEventListener('message', async (evt) => {
  const msg: { type: string; [key: string]: unknown } = evt.data;

  switch (msg.type) {
    case 'REQUEST_PLAN': {
      console.log('[companion] Watch requested plan');
      const plan = await fetchWorkoutPlan();
      if (plan) {
        state.lastPlan = plan;
        state.backendReachable = true;
      }
      sendToWatch(plan ? { type: 'PLAN_LOADED', plan } : { type: 'PLAN_LOAD_ERROR', error: 'Failed to fetch plan' });
      break;
    }

    case 'SET_COMPLETE': {
      const event: WatchEvent = {
        id: msg.eventId as string,
        type: 'SET_COMPLETE',
        payload: {
          exerciseIndex: msg.exerciseIndex,
          setIndex: msg.setIndex,
          exerciseTemplateId: msg.exerciseTemplateId,
          weightKg: msg.weightKg,
          reps: msg.reps,
          rir: msg.rir,
        },
        timestamp: Date.now(),
      };

      const ok = await postEvents([event]);
      if (!ok) {
        state.pendingEvents.push(event);
        sendToWatch({ type: 'SET_ACK', eventId: event.id, serverConfirmed: false, offline: true });
      } else {
        sendToWatch({ type: 'SET_ACK', eventId: event.id, serverConfirmed: true });
      }
      break;
    }

    case 'FINISH_WORKOUT': {
      console.log('[companion] Workout finished, syncing to Hevy...');
      const hevyWorkoutId = await completeWorkout(
        msg.startedAt as string,
        msg.endedAt as string,
      );
      if (hevyWorkoutId) {
        sendToWatch({ type: 'SYNC_SUCCESS', hevyWorkoutId });
      } else {
        sendToWatch({ type: 'SYNC_ERROR', error: 'Failed to create Hevy workout' });
      }
      break;
    }

    case 'HEARTBEAT': {
      // Periodic connectivity check from watch
      const reachable = state.backendReachable;
      try {
        await fetch(`${BACKEND_BASE_URL}/api/workout-plan/${USER_ID}`, { method: 'HEAD' });
        state.backendReachable = true;
      } catch {
        state.backendReachable = false;
      }
      sendToWatch({ type: 'HEARTBEAT_ACK', backendReachable: state.backendReachable });
      break;
    }

    default:
      console.log(`[companion] Unknown message type: ${msg.type}`);
  }
});

// ─── Messaging: Companion → Watch ────────────────────────────────────

function sendToWatch(data: Record<string, unknown>): void {
  if (peerSocket.readyState === peerSocket.OPEN) {
    peerSocket.send(data);
  } else {
    console.error('[companion] peerSocket not open, dropping message');
  }
}

// ─── Connectivity events ─────────────────────────────────────────────

peerSocket.addEventListener('open', () => {
  state.connected = true;
  console.log('[companion] Connected to watch');

  // Flush pending events on reconnect
  if (state.pendingEvents.length > 0) {
    postEvents([...state.pendingEvents]).then((ok) => {
      if (ok) {
        state.pendingEvents.forEach((e) =>
          sendToWatch({ type: 'EVENT_ACK', eventId: e.id }),
        );
        state.pendingEvents = [];
      }
    });
  }
});

peerSocket.addEventListener('close', () => {
  state.connected = false;
  console.log('[companion] Watch disconnected');
});

peerSocket.addEventListener('error', (err: any) => {
  console.error(`[companion] peerSocket error: ${err}`);
});

// ─── Settings listener (configurable backend URL, userId) ────────────

settingsStorage.addEventListener('change', (evt) => {
  // In production: handle BACKEND_URL and USER_ID changes
  console.log(`[companion] Settings changed: ${evt.key}`);
});

console.log('[companion] Hevy Companion started. Device:', device.modelName);
