'use client';

/**
 * TierPage — Tenant tier & billing dashboard for the SentryAgent portal.
 *
 * Displays:
 * - Current tier name (styled badge)
 * - Daily limits table: agents, API calls, token issuances
 * - Current usage vs. limit for each metric with a visual progress bar
 * - Time until daily reset
 * - For free/pro tiers: an "Upgrade" button that calls POST /api/v1/tiers/upgrade
 *   and redirects to the returned Stripe checkoutUrl
 * - For enterprise tier: an "Enterprise — Unlimited" label with no upgrade button
 *
 * Protected route: redirects to /login when no JWT is present (via useAuth).
 *
 * @module app/settings/tier/page
 */

import React, { useEffect, useState } from 'react';
import { useAuth } from '@/hooks/useAuth';

/* -------------------------------------------------------------------------
 * Constants
 * ---------------------------------------------------------------------- */

/** The ordered sequence of upgradeable tiers. */
const UPGRADE_PATH: Record<string, string> = {
  free: 'pro',
  pro: 'enterprise',
};

/** Human-readable tier labels. */
const TIER_LABELS: Record<string, string> = {
  free: 'Free',
  pro: 'Pro',
  enterprise: 'Enterprise',
};

/** Tailwind badge colour classes per tier. */
const TIER_BADGE_CLASSES: Record<string, string> = {
  free: 'bg-slate-100 text-slate-700',
  pro: 'bg-brand-100 text-brand-700',
  enterprise: 'bg-emerald-100 text-emerald-700',
};

/* -------------------------------------------------------------------------
 * API response types
 * ---------------------------------------------------------------------- */

/** Per-metric limit and usage returned by GET /api/v1/tiers/status. */
interface TierMetric {
  /** Hard daily ceiling, or null when unlimited (enterprise). */
  limit: number | null;
  /** Current usage count for today. */
  used: number;
}

/** Shape returned by GET /api/v1/tiers/status. */
interface TierStatus {
  /** Canonical tier identifier: "free" | "pro" | "enterprise". */
  tier: string;
  /** Daily metrics. */
  limits: {
    agents: TierMetric;
    api_calls: TierMetric;
    token_issuances: TierMetric;
  };
  /** ISO-8601 timestamp for when the daily counters reset (UTC midnight). */
  reset_at: string;
}

/** Shape returned by POST /api/v1/tiers/upgrade. */
interface UpgradeResponse {
  /** Stripe Checkout URL to redirect the user to. */
  checkoutUrl: string;
}

/* -------------------------------------------------------------------------
 * Helpers
 * ---------------------------------------------------------------------- */

/**
 * Fetches a JSON endpoint with an Authorization bearer token.
 *
 * @param url - Absolute URL to fetch
 * @param token - JWT bearer token
 * @param options - Optional RequestInit overrides
 * @returns Parsed JSON of type T
 * @throws Error with a descriptive message on non-2xx or network failure
 */
async function fetchWithAuth<T>(
  url: string,
  token: string,
  options: RequestInit = {},
): Promise<T> {
  const res = await fetch(url, {
    ...options,
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${token}`,
      ...(options.headers ?? {}),
    },
  });
  if (!res.ok) {
    const body = (await res.json().catch(() => ({}))) as { message?: string };
    throw new Error(body.message ?? `Request failed: ${res.status}`);
  }
  return res.json() as Promise<T>;
}

/**
 * Returns the number of whole minutes (rounded up) until the given ISO-8601
 * reset timestamp.
 *
 * @param resetAt - ISO-8601 date string
 * @returns Human-readable time-until string, e.g. "3 h 42 min"
 */
function formatTimeUntilReset(resetAt: string): string {
  const msUntil = new Date(resetAt).getTime() - Date.now();
  if (msUntil <= 0) return 'resetting now';
  const totalMinutes = Math.ceil(msUntil / 60_000);
  const hours = Math.floor(totalMinutes / 60);
  const minutes = totalMinutes % 60;
  if (hours === 0) return `${minutes} min`;
  return `${hours} h ${minutes} min`;
}

/* -------------------------------------------------------------------------
 * Sub-components
 * ---------------------------------------------------------------------- */

/** Props for UsageRow. */
interface UsageRowProps {
  /** Display label, e.g. "API Calls". */
  label: string;
  /** Current usage count. */
  used: number;
  /** Hard ceiling, or null for unlimited. */
  limit: number | null;
}

/**
 * Renders a single row in the usage table with a visual progress bar.
 *
 * @param props - UsageRowProps
 * @returns JSX element
 */
function UsageRow({ label, used, limit }: UsageRowProps): React.ReactElement {
  const isUnlimited = limit === null;
  const pct = isUnlimited ? 0 : Math.min(100, Math.round((used / limit) * 100));
  const barColour =
    pct >= 90 ? 'bg-red-500' : pct >= 70 ? 'bg-amber-400' : 'bg-brand-500';

  return (
    <tr>
      <td className="px-4 py-3 text-sm font-medium text-slate-700">{label}</td>
      <td className="px-4 py-3 text-right text-sm tabular-nums text-slate-900">
        {used.toLocaleString()}
      </td>
      <td className="px-4 py-3 text-right text-sm tabular-nums text-slate-500">
        {isUnlimited ? '∞' : limit.toLocaleString()}
      </td>
      <td className="px-4 py-3">
        {isUnlimited ? (
          <span className="text-xs font-medium text-emerald-600">Unlimited</span>
        ) : (
          <div className="flex items-center gap-2">
            <div className="h-2 w-24 overflow-hidden rounded-full bg-slate-100">
              <div
                className={`h-full rounded-full transition-all ${barColour}`}
                style={{ width: `${pct}%` }}
              />
            </div>
            <span className="text-xs text-slate-400">{pct}%</span>
          </div>
        )}
      </td>
    </tr>
  );
}

/** Props for TierBadge. */
interface TierBadgeProps {
  tier: string;
}

/**
 * Renders a styled pill badge for the given tier name.
 *
 * @param props - TierBadgeProps
 * @returns JSX element
 */
function TierBadge({ tier }: TierBadgeProps): React.ReactElement {
  const colourClass =
    TIER_BADGE_CLASSES[tier] ?? 'bg-slate-100 text-slate-700';
  const label = TIER_LABELS[tier] ?? tier;
  return (
    <span
      className={`inline-block rounded-full px-3 py-1 text-sm font-semibold ${colourClass}`}
    >
      {label}
    </span>
  );
}

/** Props for ErrorBanner. */
interface ErrorBannerProps {
  message: string;
}

/**
 * Inline error banner for failed data fetches or actions.
 *
 * @param props - ErrorBannerProps
 * @returns JSX element
 */
function ErrorBanner({ message }: ErrorBannerProps): React.ReactElement {
  return (
    <p className="rounded-lg bg-red-50 px-4 py-3 text-sm text-red-700">
      {message}
    </p>
  );
}

/* -------------------------------------------------------------------------
 * Page component
 * ---------------------------------------------------------------------- */

/**
 * Renders the Tier & Billing settings page.
 *
 * Checks authentication via `useAuth` (redirects to /login if no token).
 * Fetches tier status from the AgentIdP API using the stored JWT.
 *
 * @returns JSX element
 */
export default function TierPage(): React.ReactElement {
  const { token, loading: authLoading } = useAuth(true);

  const apiUrl = process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3000';

  const [status, setStatus] = useState<TierStatus | null>(null);
  const [fetchLoading, setFetchLoading] = useState(true);
  const [fetchError, setFetchError] = useState<string | null>(null);

  const [upgrading, setUpgrading] = useState(false);
  const [upgradeError, setUpgradeError] = useState<string | null>(null);

  useEffect(() => {
    if (authLoading || token === null) return;

    void fetchWithAuth<TierStatus>(`${apiUrl}/api/v1/tiers/status`, token)
      .then((data) => {
        setStatus(data);
        setFetchLoading(false);
      })
      .catch((err: unknown) => {
        setFetchError(
          err instanceof Error ? err.message : 'Failed to load tier status',
        );
        setFetchLoading(false);
      });
  }, [authLoading, token, apiUrl]);

  /**
   * Initiates a tier upgrade by calling POST /api/v1/tiers/upgrade and
   * redirecting the browser to the returned Stripe Checkout URL.
   */
  async function handleUpgrade(): Promise<void> {
    if (token === null || status === null) return;

    const targetTier = UPGRADE_PATH[status.tier];
    if (targetTier === undefined) return;

    setUpgrading(true);
    setUpgradeError(null);

    try {
      const { checkoutUrl } = await fetchWithAuth<UpgradeResponse>(
        `${apiUrl}/api/v1/tiers/upgrade`,
        token,
        {
          method: 'POST',
          body: JSON.stringify({ target_tier: targetTier }),
        },
      );
      window.location.href = checkoutUrl;
    } catch (err: unknown) {
      setUpgradeError(
        err instanceof Error ? err.message : 'Upgrade failed — please try again',
      );
      setUpgrading(false);
    }
  }

  /* ------------------------------------------------------------------
   * Render: auth loading
   * ---------------------------------------------------------------- */
  if (authLoading) {
    return (
      <div className="flex min-h-[60vh] items-center justify-center">
        <p className="text-slate-500">Loading…</p>
      </div>
    );
  }

  // token === null means useAuth is redirecting to /login; render nothing
  if (token === null) {
    return <></>;
  }

  /* ------------------------------------------------------------------
   * Render: data loading / error
   * ---------------------------------------------------------------- */
  return (
    <div className="px-6 py-16">
      <div className="mx-auto max-w-3xl">
        {/* Page header */}
        <div className="mb-10">
          <h1 className="text-4xl font-extrabold text-slate-900">
            Tier &amp; Billing
          </h1>
          <p className="mt-2 text-slate-600">
            Your current plan, daily limits, and usage at a glance
          </p>
        </div>

        {fetchError !== null && <ErrorBanner message={fetchError} />}

        {fetchLoading && fetchError === null && (
          <div className="animate-pulse space-y-4">
            <div className="h-8 w-32 rounded-full bg-slate-100" />
            <div className="h-48 rounded-2xl bg-slate-100" />
          </div>
        )}

        {status !== null && (
          <div className="space-y-6">
            {/* Current tier card */}
            <div className="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm">
              <div className="flex items-center justify-between">
                <div>
                  <p className="mb-1 text-sm font-medium text-slate-500">
                    Current Plan
                  </p>
                  <TierBadge tier={status.tier} />
                </div>

                {/* Enterprise: show label; free/pro: show upgrade button */}
                {status.tier === 'enterprise' ? (
                  <span className="text-sm font-semibold text-emerald-600">
                    Enterprise — Unlimited
                  </span>
                ) : (
                  <div className="flex flex-col items-end gap-2">
                    {upgradeError !== null && (
                      <ErrorBanner message={upgradeError} />
                    )}
                    <button
                      type="button"
                      onClick={() => { void handleUpgrade(); }}
                      disabled={upgrading}
                      className="rounded-lg bg-brand-600 px-4 py-2 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-brand-700 disabled:cursor-not-allowed disabled:opacity-50"
                    >
                      {upgrading
                        ? 'Redirecting…'
                        : `Upgrade to ${TIER_LABELS[UPGRADE_PATH[status.tier] ?? ''] ?? 'Next Tier'}`}
                    </button>
                  </div>
                )}
              </div>
            </div>

            {/* Usage table card */}
            <div className="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm">
              <h2 className="mb-4 text-lg font-semibold text-slate-900">
                Daily Usage
              </h2>

              <div className="overflow-hidden rounded-xl border border-slate-200">
                <table className="w-full text-sm">
                  <thead>
                    <tr className="bg-slate-50">
                      <th className="px-4 py-3 text-left font-semibold text-slate-700">
                        Metric
                      </th>
                      <th className="px-4 py-3 text-right font-semibold text-slate-700">
                        Used
                      </th>
                      <th className="px-4 py-3 text-right font-semibold text-slate-700">
                        Limit
                      </th>
                      <th className="px-4 py-3 text-left font-semibold text-slate-700">
                        Usage
                      </th>
                    </tr>
                  </thead>
                  <tbody className="divide-y divide-slate-100">
                    <UsageRow
                      label="Agents"
                      used={status.limits.agents.used}
                      limit={status.limits.agents.limit}
                    />
                    <UsageRow
                      label="API Calls"
                      used={status.limits.api_calls.used}
                      limit={status.limits.api_calls.limit}
                    />
                    <UsageRow
                      label="Token Issuances"
                      used={status.limits.token_issuances.used}
                      limit={status.limits.token_issuances.limit}
                    />
                  </tbody>
                </table>
              </div>

              <p className="mt-3 text-xs text-slate-400">
                Counters reset in{' '}
                <span className="font-medium text-slate-600">
                  {formatTimeUntilReset(status.reset_at)}
                </span>{' '}
                (UTC midnight)
              </p>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}
