// @ts-nocheck
import { getDeviceId } from '../lib/device';

export type UploadError = Error & {
  code?: string;
  status?: number;
  meta?: Record<string, unknown>;
};

function getCsrfToken(): string | null {
  // Method 1: Meta tag (preferred for SPA)
  const metaToken = document.querySelector('meta[name="csrf-token"]');
  if (metaToken) {
    return (metaToken as HTMLMetaElement).getAttribute('content') || null;
  }
  
  // Method 2: XSRF-TOKEN cookie (Sanctum fallback)
  const name = 'XSRF-TOKEN=';
  const decodedCookie = decodeURIComponent(document.cookie);
  const ca = decodedCookie.split(';');
  for (let i = 0; i < ca.length; i++) {
    const c = ca[i].trimStart();
    if (c.startsWith(name)) {
      const token = c.substring(name.length);
      try {
        // Decode base64 if needed
        return decodeURIComponent(atob(token));
      } catch {
        return token;
      }
    }
  }
  
  console.warn('No CSRF token found - API requests may fail');
  return null;
}

function getCsrfHeaders(): Record<string, string> {
  const token = getCsrfToken();
  const headers: Record<string, string> = {
    'X-Device-Id': getDeviceId(),
    'Accept': 'application/json',
  };
  
  if (token) {
    headers['X-CSRF-TOKEN'] = token;
    headers['X-XSRF-TOKEN'] = token;
  }
  
  return headers;
}

export async function likePhoto(id: number): Promise<number> {
  const headers = getCsrfHeaders();
  
  const res = await fetch(`/api/v1/photos/${id}/like`, {
    method: 'POST',
    credentials: 'include',
    headers: {
      ...headers,
      'Content-Type': 'application/json',
    },
  });
  
  if (!res.ok) {
    let payload: unknown = null;
    try {
      payload = await res.clone().json();
    } catch (error) {
      console.warn('Like photo: failed to parse error payload', error);
    }

    if (res.status === 419) {
      const error: UploadError = new Error('CSRF token mismatch. Please refresh the page and try again.');
      error.code = 'csrf_mismatch';
      error.status = res.status;
      throw error;
    }

    const error: UploadError = new Error(
      (payload as { error?: { message?: string } } | null)?.error?.message ?? `Like failed: ${res.status}`
    );
    error.code = (payload as { error?: { code?: string } } | null)?.error?.code ?? 'like_failed';
    error.status = res.status;
    const meta = (payload as { error?: { meta?: Record<string, unknown> } } | null)?.error?.meta;
    if (meta) {
      error.meta = meta;
    }

    throw error;
  }

  const json = await res.json();
  return json.likes_count ?? json.data?.likes_count ?? 0;
}

type UploadOptions = {
  guestName?: string;
  onProgress?: (percent: number) => void;
  signal?: AbortSignal;
  maxRetries?: number;
  onRetry?: (attempt: number) => void;
  liveShowOptIn?: boolean;
};

export async function uploadPhoto(
  eventToken: string,
  file: File,
  taskId?: number,
  emotionSlug?: string,
  options: UploadOptions = {}
): Promise<number> {
  const formData = new FormData();
  formData.append('photo', file, file.name || `photo-${Date.now()}.jpg`);
  if (taskId) formData.append('task_id', taskId.toString());
  if (emotionSlug) formData.append('emotion_slug', emotionSlug);
  if (options.guestName) formData.append('guest_name', options.guestName);
  if (typeof options.liveShowOptIn === 'boolean') {
    formData.append('live_show_opt_in', options.liveShowOptIn ? '1' : '0');
  }
  formData.append('device_id', getDeviceId());

  const maxRetries = options.maxRetries ?? 2;
  const url = `/api/v1/events/${encodeURIComponent(eventToken)}/upload`;
  const headers = getCsrfHeaders();

  const attemptUpload = (): Promise<Record<string, unknown>> =>
    new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      xhr.open('POST', url, true);
      xhr.withCredentials = true;
      xhr.responseType = 'json';

      Object.entries(headers).forEach(([key, value]) => {
        xhr.setRequestHeader(key, value);
      });

      if (options.signal) {
        const onAbort = () => xhr.abort();
        options.signal.addEventListener('abort', onAbort, { once: true });
      }

      xhr.upload.onprogress = (event) => {
        if (event.lengthComputable && options.onProgress) {
          const percent = Math.min(99, Math.round((event.loaded / event.total) * 100));
          options.onProgress(percent);
        }
      };

      xhr.onload = () => {
        const status = xhr.status;
        const payload = (xhr.response ?? null) as Record<string, unknown> | null;

        if (status >= 200 && status < 300) {
          resolve(payload);
          return;
        }

        const error: UploadError = new Error(
          (payload as { error?: { message?: string } } | null)?.error?.message ?? `Upload failed: ${status}`
        );
        error.code = (payload as { error?: { code?: string } } | null)?.error?.code ?? (status === 0 ? 'network_error' : 'upload_failed');
        error.status = status;
        const meta = (payload as { error?: { meta?: Record<string, unknown> } } | null)?.error?.meta;
        if (meta) {
          error.meta = meta;
        }
        reject(error);
      };

      xhr.onerror = () => {
        const error: UploadError = new Error('Network error during upload');
        error.code = 'network_error';
        reject(error);
      };

      xhr.ontimeout = () => {
        const error: UploadError = new Error('Upload timed out');
        error.code = 'timeout';
        reject(error);
      };

      xhr.send(formData);
    });

  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      const json = await attemptUpload();
      const payload = json as { photo_id?: number; id?: number; data?: { id?: number } };
      return payload.photo_id ?? payload.id ?? payload.data?.id ?? 0;
    } catch (error) {
      const err = error as UploadError;

      if (attempt < maxRetries && (err.code === 'network_error' || (err.status ?? 0) >= 500)) {
        options.onRetry?.(attempt + 1);
        const delay = 300 * (attempt + 1);
        await new Promise((resolve) => setTimeout(resolve, delay));
        continue;
      }

      // Map CSRF mismatch specifically for caller handling
      if ((err.status ?? 0) === 419) {
        err.code = 'csrf_mismatch';
      }

      // Flag common validation failure for file size/validation
      if ((err.status ?? 0) === 422 && !err.code) {
        err.code = 'validation_error';
      }

      throw err;
    }
  }

  throw new Error('Upload failed after retries');
}

export async function createPhotoShareLink(eventToken: string, photoId: number): Promise<{ slug: string; url: string; expires_at?: string }> {
  const headers = getCsrfHeaders();

  const res = await fetch(`/api/v1/events/${encodeURIComponent(eventToken)}/photos/${photoId}/share`, {
    method: 'POST',
    credentials: 'include',
    headers,
  });

  if (!res.ok) {
    let payload: unknown = null;
    try {
      payload = await res.clone().json();
    } catch (error) {
      console.warn('Share link error payload parse failed', error);
    }

    const errorPayload = payload as { error?: { message?: string; code?: string } } | null;
    const error: UploadError = new Error(errorPayload?.error?.message ?? 'Share link creation failed');
    error.code = errorPayload?.error?.code ?? 'share_failed';
    error.status = res.status;
    throw error;
  }

  return res.json();
}

export async function fetchPhotoShare(slug: string) {
  const res = await fetch(`/api/v1/photo-shares/${encodeURIComponent(slug)}`, {
    headers: { Accept: 'application/json' },
  });

  if (!res.ok) {
    const payload = await res.json().catch(() => null);
    const error: UploadError = new Error(payload?.error?.message ?? 'Share link unavailable');
    error.code = payload?.error?.code ?? 'share_unavailable';
    error.status = res.status;
    throw error;
  }

  const payload = await res.json();

  const normalize = (value: unknown, fallback: string): string => {
    if (typeof value === 'string' && value.trim() !== '') {
      return value;
    }
    if (value && typeof value === 'object') {
      const obj = value as Record<string, unknown>;
      const preferred = ['de', 'en'];
      for (const key of preferred) {
        const candidate = obj[key];
        if (typeof candidate === 'string' && candidate.trim() !== '') {
          return candidate;
        }
      }
      const firstString = Object.values(obj).find((candidate) => typeof candidate === 'string' && candidate.trim() !== '');
      if (typeof firstString === 'string') {
        return firstString;
      }
    }
    return fallback;
  };

  if (payload?.event) {
    payload.event = {
      ...payload.event,
      name: normalize(payload.event?.name, 'Fotospiel Event'),
    };
  }

  return payload;
}
