import React, { useCallback, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';

import { AiReadinessBanner } from '../components/dashboard/AiReadinessBanner';
import { CreditBalanceBanner } from '../components/dashboard/CreditBalanceBanner';
import { LatestArticles } from '../components/dashboard/LatestArticles';
import { LatestGaps } from '../components/dashboard/LatestGaps';
import { LatestInsights } from '../components/dashboard/LatestInsights';
import { OverviewStatCards } from '../components/dashboard/OverviewStatCards';
import {
  ScoreBreakdown,
  type ScoreRow,
} from '../components/dashboard/ScoreBreakdown';
import { ScoreRing } from '../components/dashboard/ScoreRing';
import { SetupProgressStepper } from '../components/dashboard/SetupProgressStepper';
import { TrialBanner } from '../components/dashboard/TrialBanner';
import { useAppStateContext } from '../context/user.data.context';
import { recomaze_ai_personalization_env } from '../env';
import { useCopilotDataChanged } from '../hooks/useCopilotDataChanged';
import {
  buildGreeting,
  computeAgentQuality,
  computeReadinessScore,
  displayNameFromDomain,
} from '../lib/dashboard-score';
import BaseAgentService from '../service/BaseAgentService';
import { fetchCopilotQuests } from '../service/copilot/copilot.service';
import type { ICreditBalance } from '../service/credits/credits.interface';
import { getCreditBalance } from '../service/credits/credits.service';
import {
  MINUTES_PER_STEP,
  WORDPRESS_STEPS,
} from '../service/setup-progress/setup-progress.constants';
import { getSetupProgress } from '../service/setup-progress/setup-progress.service';
import type { ISetupProgress } from '../service/setup-progress/setup-progress.interface';

interface IDashboardSummaryVisibility {
  has_scan: boolean;
  visibility_score: number;
  total_queries: number;
  queries_with_brand: number;
  share_of_voice: number;
}

interface IDashboardSummaryCatalog {
  has_job: boolean;
  state: 'pending' | 'processing' | 'completed' | 'failed' | null;
  overall_score: number;
  total_products: number;
  processed_products: number;
  critical_count: number;
}

interface IDashboardSummaryAgent {
  total_conversations: number;
  agent_gap_count: number;
}

interface IDashboardSummary {
  visibility: IDashboardSummaryVisibility;
  catalog: IDashboardSummaryCatalog;
  agent: IDashboardSummaryAgent;
}

const EMPTY_DASHBOARD_SUMMARY: IDashboardSummary = {
  visibility: {
    has_scan: false,
    visibility_score: 0,
    total_queries: 0,
    queries_with_brand: 0,
    share_of_voice: 0,
  },
  catalog: {
    has_job: false,
    state: null,
    overall_score: 0,
    total_products: 0,
    processed_products: 0,
    critical_count: 0,
  },
  agent: { total_conversations: 0, agent_gap_count: 0 },
};

const Dashboard = (): JSX.Element => {
  const { user, token, clientId } = useAppStateContext();
  const navigate = useNavigate();

  const [setupProgress, setSetupProgress] = useState<ISetupProgress | null>(
    null
  );
  const [summary, setSummary] = useState<IDashboardSummary>(
    EMPTY_DASHBOARD_SUMMARY
  );
  const [liveCredits, setLiveCredits] = useState<ICreditBalance>({});
  // Quests pillar (5% of the score); stays 0 until the copilot quest overview loads.
  const [questPillarScore, setQuestPillarScore] = useState<number>(0);

  useEffect(() => {
    if (!token) return;
    let cancelled = false;
    void (async () => {
      try {
        const balance = await getCreditBalance(token, clientId);
        if (!cancelled) setLiveCredits(balance);
      } catch {
        // Benign: credit balance unavailable for this store.
      }
    })();

    return () => {
      cancelled = true;
    };
  }, [token, clientId]);

  const refreshQuestScore = useCallback(async (): Promise<void> => {
    if (!token || !clientId) return;
    const overview = await fetchCopilotQuests(token, clientId);
    setQuestPillarScore(overview?.quest_pillar_score ?? 0);
  }, [token, clientId]);

  useEffect(() => {
    void refreshQuestScore();
  }, [refreshQuestScore]);

  const refreshSetupProgress = useCallback(async (): Promise<void> => {
    if (!token) return;
    const fresh: ISetupProgress | null = await getSetupProgress();
    if (fresh) setSetupProgress(fresh);
  }, [token]);

  useEffect(() => {
    refreshSetupProgress();
  }, [refreshSetupProgress]);

  const refreshSummary = useCallback((): void => {
    if (!token) return;
    BaseAgentService.get('ai-readiness/dashboard-summary')
      .then((response: { data?: IDashboardSummary }) =>
        setSummary(response?.data ?? EMPTY_DASHBOARD_SUMMARY)
      )
      .catch(() => setSummary(EMPTY_DASHBOARD_SUMMARY));
  }, [token]);

  useEffect(() => {
    refreshSummary();
  }, [refreshSummary]);

  // A copilot write (e.g. a catalog content check) updates the Catalog Quality
  // pillar and the overall AI Readiness score shown here, so re-pull the
  // dashboard summary and setup progress when the copilot signals a change.
  useCopilotDataChanged(() => {
    refreshSummary();
    void refreshSetupProgress();
    void refreshQuestScore();
  });

  // The authoritative /v2/credits/balance endpoint wins over the user snapshot
  // so the credit banner always reflects the merchant's real allowance.
  const credits: ICreditBalance = {
    ...(user?.credits ?? {}),
    ...liveCredits,
  };
  const domain: string = recomaze_ai_personalization_env?.domain ?? '';
  const displayName: string = displayNameFromDomain(domain) || user?.name || '';
  const greeting: string = buildGreeting(new Date(), displayName);

  const visibilityScore: number = summary.visibility.visibility_score;
  // Trust the backend's own scan flag rather than re-deriving "has a scan"
  // from query counts on the client.
  const visibilityHasScan: boolean = summary.visibility.has_scan;

  const catalog = summary.catalog;
  const catalogCompleted: boolean = catalog.state === 'completed';
  const catalogRunning: boolean =
    catalog.state === 'pending' || catalog.state === 'processing';
  const catalogScore: number = catalogCompleted ? catalog.overall_score : 0;

  const totalConversations: number = summary.agent.total_conversations;
  const conversationGapCount: number = summary.agent.agent_gap_count;

  const agentScore: number = computeAgentQuality({
    totalConversations,
    gapCount: conversationGapCount,
  });
  const { total, grade } = computeReadinessScore({
    visibility: visibilityScore,
    catalog: catalogScore,
    agent: agentScore,
    quests: questPillarScore,
  });

  const totalQueries: number = summary.visibility.total_queries;
  const queriesWithBrand: number = summary.visibility.queries_with_brand;
  const shareOfVoiceRaw: number = summary.visibility.share_of_voice;
  const sharePct: number =
    shareOfVoiceRaw <= 1 ? shareOfVoiceRaw * 100 : shareOfVoiceRaw;

  const visibilityTooltip: string =
    'AEO score: how often AI search engines (ChatGPT, Gemini, Perplexity, Claude) mention your brand on the prompts we test. Weighted 45% of the AI Readiness score.';

  const visibilityRow: ScoreRow = visibilityHasScan
    ? {
        label: 'AI Visibility',
        tag: 'AEO',
        tooltip: visibilityTooltip,
        score: visibilityScore,
        context:
          totalQueries > 0
            ? `Appearing in ${queriesWithBrand} of ${totalQueries} test queries · ${sharePct.toFixed(0)}% share of voice`
            : 'Latest scan ready.',
        action: { kind: 'link', label: 'Improve', to: '/ai-visibility' },
      }
    : {
        label: 'AI Visibility',
        tag: 'AEO',
        tooltip: visibilityTooltip,
        score: 0,
        context: 'Set up tracking prompts to start measuring visibility.',
        action: {
          kind: 'link',
          label: 'Finish setup',
          to: '/ai-visibility?settings=open',
          primary: true,
        },
      };

  const catalogTotal: number = catalog.total_products;
  const catalogProcessed: number = catalog.processed_products;
  const catalogCritical: number = catalog.critical_count;

  const catalogContext: string = catalogCompleted
    ? catalogCritical > 0
      ? `${catalogProcessed} of ${catalogTotal} products checked · ${catalogCritical} need fixes`
      : `${catalogProcessed} of ${catalogTotal} products checked`
    : catalogRunning
      ? `Checking products... ${catalogProcessed}/${catalogTotal}`
      : 'Run a content check on 50 random products to score your catalog.';

  const dispatchContentCheck = async (): Promise<void> => {
    if (!token) return;
    const response: {
      data?: { job_id?: string };
      success?: boolean;
    } = await BaseAgentService.post('ai-readiness/content-check', {
      plan: 'FREE',
    });
    if (response?.success !== false) navigate('/catalog-quality');
  };

  const catalogRow: ScoreRow = {
    label: 'Catalog Quality',
    tag: 'GEO',
    tooltip:
      'GEO score: how well your product titles and descriptions match the structure AI shopping assistants need to recommend you. Calibrated on a 50-product audit. Weighted 45% of the AI Readiness score.',
    score: catalogScore,
    context: catalogContext,
    action: catalogCompleted
      ? { kind: 'link', label: 'View report', to: '/catalog-quality' }
      : catalogRunning
        ? {
            kind: 'link',
            label: 'View progress',
            to: '/catalog-quality',
            primary: true,
          }
        : {
            kind: 'button',
            label: 'Run check',
            busyLabel: 'Dispatching...',
            primary: true,
            onClick: dispatchContentCheck,
          },
  };

  const agentRow: ScoreRow = {
    label: 'Agent Quality',
    tooltip:
      "How often the assistant handled customer requests without saying 'I do not know', 'we do not have that', or routing the customer elsewhere. Score = (1 - gaps / conversations) * 100. Weighted 5% of the AI Readiness score.",
    score: agentScore,
    context:
      totalConversations > 0
        ? `${conversationGapCount} conversation gaps detected across ${totalConversations} conversations`
        : 'Get your first conversations to unlock this score.',
    action: { kind: 'link', label: 'Improve', to: '/agent-analytics?tab=gaps' },
  };

  const questsRow: ScoreRow = {
    label: 'Quests',
    tooltip:
      'Daily quests you complete with Recomaze Co-pilot (setup, content, and fix tasks). Weighted 5% of the AI Readiness score.',
    score: questPillarScore,
    context:
      questPillarScore > 0
        ? 'Keep completing daily quests with Recomaze Co-pilot.'
        : 'Complete daily quests with Recomaze Co-pilot to earn this.',
  };

  const planLabel: string | null = user?.plan_snapshot
    ? formatPlanLabel(String(user.plan_snapshot))
    : null;

  return (
    <div className="w-full lg:w-11/12 xl:w-10/12 mx-auto p-5 space-y-6">
      <AiReadinessBanner
        score={total}
        grade={grade}
        fixCount={conversationGapCount}
      />

      <header className="flex flex-wrap items-end justify-between gap-3">
        <div>
          <h1 className="text-3xl font-bold tracking-tight text-gray-900">
            {greeting}
          </h1>
          <p className="mt-1 text-sm text-gray-500">
            {[
              domain || displayName,
              planLabel ? `${planLabel} trial` : null,
              typeof user?.trial_days === 'number' && user.trial_days > 0
                ? `Day ${Math.max(1, Math.min(7, 7 - user.trial_days + 1))} of trial`
                : null,
            ]
              .filter(Boolean)
              .join(' · ')}
          </p>
        </div>
      </header>

      <TrialBanner
        trialDays={user?.trial_days ?? 0}
        planLabel={planLabel ? `${planLabel} tier` : null}
        creditsUsed={credits.used ?? 0}
        creditsGranted={credits.granted ?? 0}
      />

      <CreditBalanceBanner
        used={credits.used ?? 0}
        granted={credits.granted ?? 0}
        balance={credits.balance}
      />

      <section className="rounded-2xl border border-gray-200 bg-white p-6">
        <div className="grid gap-6 lg:grid-cols-[280px_1fr] lg:items-center">
          <div className="flex flex-col items-center gap-4">
            <ScoreRing score={total} grade={grade} />
            <p className="max-w-[14rem] text-center text-xs leading-relaxed text-gray-500">
              AI traffic grew{' '}
              <strong className="font-semibold text-gray-900">393%</strong> last
              year, converts{' '}
              <strong className="font-semibold text-gray-900">
                42% better
              </strong>
              , and only{' '}
              <strong className="font-semibold text-gray-900">13%</strong> of
              retailers are ready to capture it. Your score shows{' '}
              <strong className="font-semibold text-gray-900">
                how ready you are
              </strong>
              , across three essential dimensions.
            </p>
          </div>
          <ScoreBreakdown
            rows={[visibilityRow, catalogRow, agentRow, questsRow]}
          />
        </div>
      </section>

      {!isSetupComplete(setupProgress, WORDPRESS_STEPS) && (
        <SetupProgressStepper
          progress={setupProgress}
          steps={WORDPRESS_STEPS}
          estimatedMinutesLeft={estimateMinutesLeft(
            setupProgress,
            WORDPRESS_STEPS
          )}
        />
      )}

      {token && clientId && (
        <>
          <OverviewStatCards token={token} />
          <LatestInsights token={token} />
          <LatestGaps token={token} />
          <LatestArticles clientId={clientId} token={token} />
        </>
      )}
    </div>
  );
};

/**
 * @param {string} plan
 * @return {string}
 */
function formatPlanLabel(plan: string): string {
  return plan
    .toLowerCase()
    .split('_')
    .map(planWord => planWord.charAt(0).toUpperCase() + planWord.slice(1))
    .join(' ');
}

/**
 * @param {ISetupProgress | null} progress
 * @param {ReadonlyArray<{ key: string }>} platformSteps
 * @return {number}
 */
function estimateMinutesLeft(
  progress: ISetupProgress | null,
  platformSteps: ReadonlyArray<{ key: string }>
): number {
  if (!progress?.steps) return platformSteps.length * MINUTES_PER_STEP;
  let priorComplete: boolean = true;
  let remaining: number = 0;
  for (const stepDefinition of platformSteps) {
    const stored: boolean =
      progress.steps[stepDefinition.key]?.completed ?? false;
    const effective: boolean = priorComplete && stored;
    if (!effective) remaining += 1;
    priorComplete = effective;
  }

  return remaining * MINUTES_PER_STEP;
}

/**
 * Whether every onboarding step is effectively complete. Uses the same
 * sequential gating as the stepper (a step only counts once it and every
 * earlier step are done), so the Dashboard can hide the stepper once the
 * merchant reaches the last step.
 *
 * @param {ISetupProgress | null} progress
 * @param {ReadonlyArray<{ key: string }>} platformSteps
 * @return {boolean}
 */
function isSetupComplete(
  progress: ISetupProgress | null,
  platformSteps: ReadonlyArray<{ key: string }>
): boolean {
  if (!progress?.steps || platformSteps.length === 0) return false;
  let priorComplete: boolean = true;
  for (const stepDefinition of platformSteps) {
    const stored: boolean =
      progress.steps[stepDefinition.key]?.completed ?? false;
    priorComplete = priorComplete && stored;
    if (!priorComplete) return false;
  }

  return true;
}

export default Dashboard;
