import { getAttribution, trackConversion, trackEvent, type LeadTrackingPayload } from '@/lib/tracking';
import { getPaidProductMetadata } from '@/lib/offer';
import type {
  PublicForecastRouteResponseV1,
  PublicPlayerPropsResponseV1,
  PublicTopPicksResponseV1,
  PublicTopPropsResponseV1,
} from '@/contracts/forecast-public';

const API_URL = process.env.NEXT_PUBLIC_API_URL || 'https://rainmakersports.app/api';
const AUTH_STATE_COOKIE = 'rm_logged_in';
const AUTH_STATE_MAX_AGE_SECONDS = 24 * 60 * 60;
export const AUTH_LOST_EVENT = 'rm-auth-lost';

export type SocialFeedMode = 'posted' | 'preview';

export interface SocialFeedResponse<T = unknown> {
  data: T[];
  feed_mode: SocialFeedMode;
  pagination: {
    page: number;
    limit: number;
    total: number;
    pages: number;
  };
}

export interface EventsApiEvent {
  id: string;
  league: string;
  homeTeam: string;
  homeShort: string;
  awayTeam: string;
  awayShort: string;
  startsAt: string;
  oddsUpdatedAt?: string | null;
  verificationLabel?: string | null;
  verificationType?: 'sportsbook' | 'feed' | 'provider' | 'unknown' | null;
  status: string;
  moneyline: { home: number | null; away: number | null; draw?: number | null };
  spread: { home: { line: number; odds: number | null } | null; away: { line: number; odds: number | null } | null } | any;
  total: { over: { line: number; odds: number | null } | null; under: { line: number; odds: number | null } | null } | any;
  openingSpread?: { home: { line: number; odds: number | null } | null; away: { line: number; odds: number | null } | null } | null;
  openingMoneyline?: { home: number | null; away: number | null; draw?: number | null } | null;
  openingTotal?: { over: { line: number; odds: number | null } | null; under: { line: number; odds: number | null } | null } | null;
  forecastStatus: 'ready' | 'generating';
  playerPropsCount: number;
  playerPropsAvailable: boolean;
  playerPropsStatus: 'ready' | 'empty_after_run' | 'not_generated' | 'no_source_market_candidates' | 'no_valid_source_market_matches';
  digimonLockCount: number;
  forecastMeta?: {
    confidence: number;
    edge?: number | null;
    valueRating: number;
    forecastSide?: string | null;
    hasSharpSignal: boolean;
    hasSteamSignal?: boolean;
    hasForecast: boolean;
    marketBreakdown?: Record<string, number> | null;
  } | null;
}

export interface EventsApiResponse {
  leagues: string[];
  events: Record<string, EventsApiEvent[]>;
  totalEvents: number;
}

export interface EventSportsApiSport {
  key: string;
  label: string;
  todayCount: number;
}

export interface EventSportsApiResponse {
  sports: EventSportsApiSport[];
  total: number;
}

export interface TopPicksQuery {
  league?: string | null;
  sport?: string | null;
}

async function parseResponseBody(res: Response): Promise<any> {
  const contentType = res.headers.get('content-type') || '';
  const raw = await res.text();

  if (!raw) {
    return null;
  }

  if (contentType.includes('application/json')) {
    return JSON.parse(raw);
  }

  const trimmed = raw.trim();
  if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
    return JSON.parse(trimmed);
  }

  return raw;
}

function buildApiError(res: Response, data: unknown, fallbackMessage: string): Error & { status?: number; data?: unknown } {
  const dataMessage = data && typeof data === 'object' && 'error' in data && typeof (data as any).error === 'string'
    ? (data as any).error
    : null;
  const rawMessage = typeof data === 'string' && data.trim()
    ? data.trim()
    : null;
  const err: Error & { status?: number; data?: unknown } = new Error(dataMessage || rawMessage || fallbackMessage);
  err.status = res.status;
  err.data = data;
  return err;
}

async function publicFetch(path: string, options: RequestInit = {}): Promise<any> {
  const res = await fetch(`${API_URL}${path}`, {
    ...options,
    credentials: 'include',
  });
  const data = await parseResponseBody(res);

  if (!res.ok) {
    throw buildApiError(res, data, `Request failed (${res.status})`);
  }

  return data;
}

function hasSecureContext(): boolean {
  return typeof window !== 'undefined' && window.location.protocol === 'https:';
}

function getAuthStateCookie(): string | null {
  if (typeof document === 'undefined') return null;
  const match = document.cookie.match(new RegExp(`(?:^|; )${AUTH_STATE_COOKIE}=([^;]*)`));
  return match?.[1] || null;
}

function setAuthStateMarker(authenticated: boolean): void {
  if (typeof document === 'undefined') return;
  const secure = hasSecureContext() ? ' Secure;' : '';
  try {
    document.cookie = `${AUTH_STATE_COOKIE}=${authenticated ? '1' : ''}; Path=/; Max-Age=${authenticated ? AUTH_STATE_MAX_AGE_SECONDS : 0}; SameSite=Lax;${secure}`;
  } catch {
    // Ignore cookie write failures in restricted environments.
  }
}

export function setToken(_token: string): void {
  setAuthStateMarker(true);
}

export function clearToken(): void {
  setAuthStateMarker(false);
  if (typeof window !== 'undefined') {
    window.dispatchEvent(new Event(AUTH_LOST_EVENT));
  }
  fetch(`${API_URL}/auth/logout`, {
    method: 'POST',
    credentials: 'include',
    keepalive: true,
  }).catch((err) => {
    console.error('Logout request failed:', err);
  });
}

export function isLoggedIn(): boolean {
  return getAuthStateCookie() === '1';
}

async function apiFetch(path: string, options: RequestInit = {}): Promise<any> {
  const headers: Record<string, string> = {
    'Content-Type': 'application/json',
    ...(options.headers as Record<string, string> || {}),
  };

  const res = await fetch(`${API_URL}${path}`, {
    ...options,
    headers,
    credentials: 'include',
  });

  if (res.status === 401) {
    setAuthStateMarker(false);
    if (typeof window !== 'undefined') {
      window.dispatchEvent(new Event(AUTH_LOST_EVENT));
    }
    const err: any = new Error('Authentication required');
    err.status = 401;
    throw err;
  }

  const data = await parseResponseBody(res);

  if (!res.ok) {
    throw buildApiError(res, data, 'Request failed');
  }

  return data;
}

// Auth
export const signup = (
  email: string,
  password: string,
  marketingConsent?: boolean,
  country_code?: string,
  adultConfirmed?: boolean,
  tracking?: LeadTrackingPayload,
) =>
  apiFetch('/auth/signup', {
    method: 'POST',
    body: JSON.stringify({ email, password, marketingConsent, country_code, adultConfirmed, tracking }),
  });

// Geo (public, no auth)
export const getGeoCountry = async (): Promise<{ country_code: string; country_name: string; source: string }> => {
  try {
    const data = await publicFetch('/geo/country');
    if (!data || typeof data !== 'object') {
      return { country_code: '', country_name: '', source: 'unknown' };
    }
    return data;
  } catch {
    return { country_code: '', country_name: '', source: 'unknown' };
  }
};

export const login = (email: string, password: string) =>
  apiFetch('/auth/login', { method: 'POST', body: JSON.stringify({ email, password }) });

export const getGoogleAuthUrl = (
  mode: 'login' | 'signup',
  returnTo?: string,
  adultConfirmed?: boolean,
  tracking?: LeadTrackingPayload,
) => {
  const params = new URLSearchParams({ mode });
  if (returnTo) params.set('returnTo', returnTo);
  if (mode === 'signup' && adultConfirmed) params.set('adultConfirmed', '1');
  if (mode === 'signup' && tracking) {
    params.set('eventId', tracking.eventId);
    if (tracking.ttclid) params.set('ttclid', tracking.ttclid);
    if (tracking.ttp) params.set('ttp', tracking.ttp);
    if (tracking.url) params.set('trackingUrl', tracking.url);
    if (tracking.referrer) params.set('trackingReferrer', tracking.referrer);
    if (tracking.placement) params.set('trackingPlacement', tracking.placement);
    if (tracking.method) params.set('trackingMethod', tracking.method);
    if (tracking.consent) params.set('trackingConsent', '1');
  }
  return `${API_URL}/auth/google/start?${params.toString()}`;
};

export const getMe = () => apiFetch('/auth/me');

export const forgotPassword = (email: string) =>
  apiFetch('/auth/forgot-password', { method: 'POST', body: JSON.stringify({ email }) });

export const resetPassword = (token: string, password: string) =>
  apiFetch('/auth/reset-password', { method: 'POST', body: JSON.stringify({ token, password }) });

export const resendVerification = () =>
  apiFetch('/auth/resend-verification', { method: 'POST' });

export const resendVerificationPublic = (email: string, password: string) =>
  apiFetch('/auth/resend-verification-public', {
    method: 'POST',
    body: JSON.stringify({ email, password }),
  });

// Events
export const getEvents = (league?: string): Promise<EventsApiResponse> =>
  apiFetch(`/events${league ? `?league=${league}` : ''}`);

export const getEventSports = (): Promise<EventSportsApiResponse> =>
  apiFetch('/events/sports');

// Forecasts
export const getForecast = (eventId: string, league?: string | null): Promise<PublicForecastRouteResponseV1> =>
  apiFetch(`/forecast/${eventId}${league ? `?league=${encodeURIComponent(league)}` : ''}`);

export const getForecastPreview = (eventId: string) =>
  apiFetch(`/forecast/${eventId}/preview`);

export const getForecastGameData = (eventId: string) =>
  apiFetch(`/forecast/${eventId}/game-data`);

export const getForecastPlayerDigiview = (
  eventId: string,
  params: { assetId?: string | null; player?: string | null; team?: string | null } = {},
) => {
  const query = new URLSearchParams();
  if (params.assetId) query.set('assetId', params.assetId);
  if (params.player) query.set('player', params.player);
  if (params.team) query.set('team', params.team);
  const suffix = query.toString() ? `?${query.toString()}` : '';
  return apiFetch(`/forecast/${eventId}/player-digiview${suffix}`);
};

export const getForecastMarketHistory = (eventId: string) =>
  apiFetch(`/forecast/${eventId}/market-history`);

// Purchases — Authorize.Net Accept Hosted flow
export const createCheckout = async (productType: 'daily_pass' | 'monthly_pass', quantity?: number) => {
  const clientTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
  const attribution = getAttribution();
  const productMetadata = getPaidProductMetadata(productType);
  const trackingPayload = {
    product: productType,
    plan: productType,
    quantity: quantity || 1,
    value: productMetadata?.value,
    currency: 'USD',
    content_id: productMetadata?.contentId ?? productType,
    content_name: productMetadata?.contentName ?? productType,
    content_type: 'product',
  };
  trackConversion('checkout_start', trackingPayload);
  const data = await apiFetch('/purchase/checkout', {
    method: 'POST',
    body: JSON.stringify({ productType, clientTimezone, attribution }),
  });
  trackEvent('checkout_token_created', trackingPayload);
  trackConversion('checkout_token_created', trackingPayload);
  return data;
};

/**
 * Redirect to Authorize.Net hosted payment page.
 * Creates a hidden form, sets the token, and submits.
 */
export function redirectToPayment(token: string, formUrl: string) {
  trackEvent('checkout_redirect', {
    provider: 'authnet',
    destination_host: (() => {
      try { return new URL(formUrl).host; } catch { return null; }
    })(),
  });

  const form = document.createElement('form');
  form.method = 'POST';
  form.action = formUrl;
  form.style.display = 'none';

  const tokenInput = document.createElement('input');
  tokenInput.type = 'hidden';
  tokenInput.name = 'token';
  tokenInput.value = token;
  form.appendChild(tokenInput);

  document.body.appendChild(form);
  form.submit();
}

export const verifyPurchase = (transactionId?: string, invoiceNumber?: string) =>
  apiFetch('/purchase/verify', { method: 'POST', body: JSON.stringify({ transactionId, invoiceNumber }) });

// Entitlements
export const getEntitlements = () => apiFetch('/purchase/entitlements');

// Team Props
export const getTeamProps = (eventId: string, team: 'home' | 'away', league: string) =>
  apiFetch('/forecast/unlock', {
    method: 'POST',
    body: JSON.stringify({ eventId, type: 'TEAM_PROPS', team, league }),
  });

// Insight Unlocks (Steam & Sharp)
export const unlockSteamInsight = (eventId: string, league: string) =>
  apiFetch('/forecast/unlock', {
    method: 'POST',
    body: JSON.stringify({ eventId, type: 'STEAM_INSIGHT', league }),
  });

export const unlockSharpInsight = (eventId: string, league: string) =>
  apiFetch('/forecast/unlock', {
    method: 'POST',
    body: JSON.stringify({ eventId, type: 'SHARP_INSIGHT', league }),
  });

export const unlockDvpInsight = (eventId: string, league: string) =>
  apiFetch('/forecast/unlock', {
    method: 'POST',
    body: JSON.stringify({ eventId, type: 'DVP_INSIGHT', league }),
  });

export const unlockHcwInsight = (eventId: string, league: string) =>
  apiFetch('/forecast/unlock', {
    method: 'POST',
    body: JSON.stringify({ eventId, type: 'HCW_INSIGHT', league }),
  });

// Team Props Breakdown (no credit cost, available to all logged-in users)
export const getTeamPropsBreakdown = (eventId: string): Promise<any> =>
  apiFetch(`/forecast/${eventId}/team-props-breakdown`);

// Player Props (precomputed individual props)
export const getPlayerProps = (eventId: string, team?: 'home' | 'away'): Promise<PublicPlayerPropsResponseV1> =>
  apiFetch(`/forecast/${eventId}/player-props${team ? `?team=${team}` : ''}`);

export const getTopProps = (league?: string | null, limit = 10): Promise<PublicTopPropsResponseV1> => {
  const params = new URLSearchParams();
  if (league) params.set('league', league);
  params.set('limit', String(limit));
  return apiFetch(`/forecast/top-props?${params.toString()}`);
};

export const getTopPicks = (query: TopPicksQuery = {}, limit = 12): Promise<PublicTopPicksResponseV1> => {
  const params = new URLSearchParams();
  if (query.league) params.set('league', query.league);
  if (query.sport) params.set('sport', query.sport);
  params.set('limit', String(limit));
  return apiFetch(`/forecast/top-picks?${params.toString()}`);
};

// Per-player prop unlock
export const unlockPlayerProp = (assetId: string) =>
  apiFetch(`/forecast-assets/${assetId}/unlock`, { method: 'POST' });

// Stats (public, no auth)
export const getModelStats = async () => {
  return publicFetch('/stats/model');
};

export const getSocialStats = async () => {
  return publicFetch('/stats/social');
};

// Push notifications
export const getVapidKey = async () => {
  return publicFetch('/push/vapid-key');
};

export const subscribePush = async (subscription: PushSubscriptionJSON, preferences?: any) =>
  apiFetch('/push/subscribe', { method: 'POST', body: JSON.stringify({ subscription, preferences }) });

export const unsubscribePush = async () =>
  apiFetch('/push/unsubscribe', { method: 'POST' });

export const getPushStatus = async () => apiFetch('/push/status');

// Free Pick of the Day (public, no auth)
export const getFreePick = async () => {
  return publicFetch('/free-pick');
};

// Free Pick full content (public, no auth, no deduction)
export const getFreePickContent = async () => {
  return publicFetch('/free-pick/content');
};

// Quick signup (email only)
export const quickSignup = async (email: string, adultConfirmed?: boolean, tracking?: LeadTrackingPayload) => {
  const res = await fetch(`${API_URL}/auth/quick-signup`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, adultConfirmed, tracking }),
    credentials: 'include',
  });
  const data = await res.json();
  if (!res.ok) {
    const err: any = new Error(data.error || 'Signup failed');
    err.status = res.status;
    throw err;
  }
  return data;
};

// User
export const getDashboard = () => apiFetch('/user/dashboard');
export const getUserPicks = (limit?: number) =>
  apiFetch(`/user/picks${limit ? `?limit=${limit}` : ''}`);

// CLV + KPI Stats (public, no auth)
export const getClvStats = async () => {
  return publicFetch('/stats/clv');
};

export const getClvRecent = async () => {
  return publicFetch('/stats/clv/recent');
};

export const getKpiStats = async () => {
  return publicFetch('/stats/kpi');
};

// Model Performance V2
export const getPerformanceData = async (options: { window?: number; page?: number; limit?: number; tier?: string; league?: string; kind?: 'all' | 'props' | 'games' } = {}) => {
  const params = new URLSearchParams();
  if (options.window) params.set('window', String(options.window));
  if (options.page) params.set('page', String(options.page));
  if (options.limit) params.set('limit', String(options.limit));
  if (options.tier) params.set('tier', options.tier);
  if (options.league) params.set('league', options.league);
  if (options.kind) params.set('kind', options.kind);
  return publicFetch(`/stats/performance?${params}`);
};

// Upcoming Forecasts (public, no auth)
export const getUpcomingForecasts = async () => {
  return publicFetch('/stats/upcoming');
};

// Surveys (raw fetch — works for both auth'd and unauth'd users)
export const getCurrentSurvey = async () => {
  return publicFetch('/survey/current', {
    headers: { 'Content-Type': 'application/json' },
  });
};
export const submitSurvey = (surveyId: string, answers: any) =>
  apiFetch(`/survey/${surveyId}/submit`, { method: 'POST', body: JSON.stringify({ answers }) });

// Affiliate (public, no auth)
export const getAffiliateOffers = async () => {
  return publicFetch('/affiliate/offers');
};

export const registerAffiliate = async (email: string, companyName: string, password: string) => {
  const res = await fetch(`${API_URL}/affiliate/register`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, companyName, password }),
  });
  const data = await res.json();
  if (!res.ok) throw new Error(data.error || 'Registration failed');
  return data;
};

export const loginAffiliate = async (email: string, password: string) => {
  const res = await fetch(`${API_URL}/affiliate/login`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, password }),
  });
  const data = await res.json();
  if (!res.ok) throw new Error(data.error || 'Login failed');
  return data;
};

export const getAffiliateDashboard = async (affiliateId: string) => {
  const res = await fetch(`${API_URL}/affiliate/dashboard?affiliate_id=${encodeURIComponent(affiliateId)}`);
  const data = await res.json();
  if (!res.ok) throw new Error(data.error || 'Failed to fetch dashboard');
  return data;
};

// News (public, no auth)
export const getNewsHeadlines = async (sport?: string, page?: number) => {
  const params = new URLSearchParams();
  if (sport) params.set('sport', sport);
  if (page) params.set('page', String(page));
  params.set('limit', '60');
  return publicFetch(`/news/headlines?${params}`);
};

export const getNewsTrending = async () => {
  return publicFetch('/news/trending');
};

export const getNewsSports = async () => {
  return publicFetch('/news/sports');
};

export const trackNewsClick = (id: string, type: 'external' | 'blog') => {
  fetch(`${API_URL}/news/click`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ id, type }),
  }).catch((err) => {
    console.error('Failed to track news click:', err);
  });
};

export const getNewsCurated = async (limit?: number) => {
  const params = new URLSearchParams();
  if (limit) params.set('limit', String(limit));
  return publicFetch(`/news/curated?${params}`);
};

// Social Feed (public, no auth)
export const getSocialFeed = async (persona?: string, sport?: string, page?: number): Promise<SocialFeedResponse> => {
  const params = new URLSearchParams();
  if (persona) params.set('persona', persona);
  if (sport) params.set('sport', sport);
  if (page) params.set('page', String(page));
  params.set('limit', '20');
  return publicFetch(`/social/feed?${params}`);
};

export const getSocialPersonas = async () => {
  return publicFetch('/social/personas');
};

// RainWire Unified Feed (public, no auth)
export const getRainwireFeed = async (options: {
  filter?: 'all' | 'news' | 'social';
  sport?: string;
  persona?: string;
  page?: number;
  limit?: number;
} = {}) => {
  const params = new URLSearchParams();
  if (options.filter) params.set('filter', options.filter);
  if (options.sport) params.set('sport', options.sport);
  if (options.persona) params.set('persona', options.persona);
  if (options.page) params.set('page', String(options.page));
  if (options.limit) params.set('limit', String(options.limit));
  return publicFetch(`/rainwire/feed?${params}`);
};

// Archive (public, no auth)
export const getArchiveDetail = async (eventId: string) => {
  return publicFetch(`/archive/${encodeURIComponent(eventId)}`);
};

// Clipper
export const getClips = () => apiFetch('/clipper');
export const removeClip = (clipId: string) => apiFetch(`/clipper/${clipId}`, { method: 'DELETE' });
export const clearAllClips = () => apiFetch('/clipper?all=true', { method: 'DELETE' });
export const emailClips = () => apiFetch('/clipper/email', { method: 'POST' });
export const addClip = (data: {
  clip_type: string;
  display_text: string;
  forecast_asset_id?: string;
  event_id?: string;
  entity_id?: string;
  run_date?: string;
  clip_data?: {
    forecasted_value?: number | null;
    market_value?: number | null;
    direction?: 'OVER' | 'UNDER' | 'NONE';
    edge_pct?: number | null;
    confidence_pct?: number | null;
    winner?: string | null;
    display_context?: string;
  };
}) => apiFetch('/clipper', { method: 'POST', body: JSON.stringify(data) });

// Profile
export const submitIntakeProfile = (data: { favoriteSports?: string[]; interestBuckets?: string[]; marketParticipant?: string }) =>
  apiFetch('/profile/intake', { method: 'POST', body: JSON.stringify(data) });

export const getProfile = () => apiFetch('/profile');

// Credential Update + Verify
export const updateCredentialsAndVerify = (email: string, password: string) =>
  apiFetch('/auth/update-credentials-and-verify', { method: 'POST', body: JSON.stringify({ email, password }) });
