// SPDX-License-Identifier: GPL-3.0-or-later

import { createContext, useContext, useState, useEffect, useCallback, ReactNode } from 'react';
import * as api from '../lib/api';
import type {
  StatusResponse,
  TorStatusResponse,
  ServerStatusResponse,
  AccountResponse,
  BackupRequest,
  RestoreRequest,
  Snapshot,
  Job,
  PathsResponse,
  ConfigResponse,
  ConfigUpdateRequest,
  ModeResponse,
  RetentionResponse,
  CredentialsResponse,
  CredentialsRotateResponse,
  ScheduleResponse,
  ScheduleRequest,
} from '../lib/api';

interface BackendContextType {
  // Connection state
  isConnected: boolean;
  hasEverConnected: boolean;
  isLoading: boolean;
  error: string | null;

  // Status
  status: StatusResponse | null;
  torStatus: TorStatusResponse | null;
  serverStatus: ServerStatusResponse | null;
  account: AccountResponse | null;

  // Actions — general
  refresh: () => Promise<void>;

  // Actions — backup
  startBackup: (paths?: string[], exclude?: string[], tags?: Record<string, string>) => Promise<Job>;

  // Actions — restore
  startRestore: (snapshotId: string, target: string, paths?: string[]) => Promise<Job>;

  // Actions — snapshots
  listSnapshots: () => Promise<Snapshot[]>;
  getSnapshot: (id: string) => Promise<Snapshot>;
  deleteSnapshot: (id: string) => Promise<void>;

  // Actions — jobs
  listJobs: (status?: string) => Promise<Job[]>;
  getJob: (id: string) => Promise<Job>;
  cancelJob: (jobId: string) => Promise<void>;

  // Actions — paths
  getPaths: () => Promise<PathsResponse>;
  addPath: (path: string) => Promise<PathsResponse>;
  removePath: (path: string) => Promise<PathsResponse>;

  // Actions — config
  getConfig: () => Promise<ConfigResponse>;
  updateConfig: (config: ConfigUpdateRequest) => Promise<ConfigResponse>;

  // Actions — mode
  getMode: () => Promise<ModeResponse>;
  updateMode: (mode: string, acknowledge?: boolean) => Promise<ModeResponse>;

  // Actions — retention
  getRetention: () => Promise<RetentionResponse>;

  // Actions — credentials
  getCredentials: () => Promise<CredentialsResponse>;
  rotateCredentials: () => Promise<CredentialsRotateResponse>;

  // Actions — schedules
  listSchedules: () => Promise<Record<string, ScheduleResponse>>;
  createSchedule: (name: string, schedule: ScheduleRequest) => Promise<ScheduleResponse>;
  updateSchedule: (name: string, schedule: Partial<ScheduleRequest>) => Promise<ScheduleResponse>;
  deleteSchedule: (name: string) => Promise<void>;
  triggerSchedule: (name: string) => Promise<Job>;
}

const BackendContext = createContext<BackendContextType | null>(null);

export function BackendProvider({ children }: { children: ReactNode }) {
  const [isConnected, setIsConnected] = useState(false);
  const [hasEverConnected, setHasEverConnected] = useState(false);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [status, setStatus] = useState<StatusResponse | null>(null);
  const [torStatus, setTorStatus] = useState<TorStatusResponse | null>(null);
  const [serverStatus, setServerStatus] = useState<ServerStatusResponse | null>(null);
  const [account, setAccount] = useState<AccountResponse | null>(null);

  const refresh = useCallback(async () => {
    try {
      setError(null);

      // Check health first
      await api.health();
      setIsConnected(true);
      setHasEverConnected(true);

      // Get status
      const statusData = await api.getStatus();
      setStatus(statusData);

      // Get Tor and server status in parallel
      const [torData, serverData] = await Promise.all([
        api.getTorStatus().catch(() => null),
        api.getServerStatus().catch(() => null),
      ]);
      setTorStatus(torData);
      setServerStatus(serverData);

      // Get account info (daemon handles not-registered case)
      const accountData = await api.getAccount().catch(() => null);
      setAccount(accountData);
    } catch (err) {
      setIsConnected(false);
      setError(err instanceof Error ? err.message : 'Failed to connect to backend');
      console.error('Backend connection error:', err);
    }
  }, []);

  // Initial load and periodic refresh
  useEffect(() => {
    const init = async () => {
      setIsLoading(true);
      await refresh();
      setIsLoading(false);
    };
    init();

    // Refresh every 10 seconds
    const interval = setInterval(refresh, 10000);
    return () => clearInterval(interval);
  }, [refresh]);

  // ---- Wrapped API calls ----

  const startBackup = async (paths?: string[], exclude?: string[], tags?: Record<string, string>): Promise<Job> => {
    const request: BackupRequest = { paths: paths || [], exclude, tags };
    return api.startBackup(request);
  };

  const startRestore = async (snapshotId: string, target: string, paths?: string[]): Promise<Job> => {
    const request: RestoreRequest = { snapshot_id: snapshotId, target, paths };
    return api.startRestore(request);
  };

  return (
    <BackendContext.Provider
      value={{
        isConnected,
        hasEverConnected,
        isLoading,
        error,
        status,
        torStatus,
        serverStatus,
        account,
        refresh,
        startBackup,
        startRestore,
        listSnapshots: api.listSnapshots,
        getSnapshot: api.getSnapshot,
        deleteSnapshot: api.deleteSnapshot,
        listJobs: api.listJobs,
        getJob: api.getJob,
        cancelJob: api.cancelJob,
        getPaths: api.getPaths,
        addPath: api.addPath,
        removePath: api.removePath,
        getConfig: api.getConfig,
        updateConfig: api.updateConfig,
        getMode: api.getMode,
        updateMode: api.updateMode,
        getRetention: api.getRetention,
        getCredentials: api.getCredentials,
        rotateCredentials: api.rotateCredentials,
        listSchedules: api.listSchedules,
        createSchedule: api.createSchedule,
        updateSchedule: api.updateSchedule,
        deleteSchedule: api.deleteSchedule,
        triggerSchedule: api.triggerSchedule,
      }}
    >
      {children}
    </BackendContext.Provider>
  );
}

export function useBackend() {
  const context = useContext(BackendContext);
  if (!context) {
    throw new Error('useBackend must be used within a BackendProvider');
  }
  return context;
}
