// Theme assembly. ColorScheme + typography + component themes.

import 'package:flutter/material.dart';
import 'package:flutter_markdown_plus/flutter_markdown_plus.dart';

import 'tokens.dart';

class FaiTheme {
  FaiTheme._();

  /// Bundled font families. Declared in pubspec.yaml `fonts:` and
  /// shipped as TTF assets under `assets/fonts/` — NOT fetched at
  /// runtime via google_fonts. This keeps the app working fully
  /// offline / air-gapped (KRITIS) and avoids the font-swap flash
  /// google_fonts shows on first paint.
  static const String _uiFamily = 'Inter';
  static const String _monoFamily = 'JetBrains Mono';

  /// Inter for UI, JetBrains Mono for technical strings (IDs,
  /// paths, capability refs). Both bundled (see [_uiFamily]).
  static TextTheme _textTheme(Brightness b) {
    final base = b == Brightness.dark
        ? Typography.whiteCupertino
        : Typography.blackCupertino;
    return base.apply(fontFamily: _uiFamily).copyWith(
      displaySmall: const TextStyle(
        fontFamily: _uiFamily,
        fontSize: 28,
        fontWeight: FontWeight.w600,
        letterSpacing: -0.4,
      ),
      headlineSmall: const TextStyle(
        fontFamily: _uiFamily,
        fontSize: 20,
        fontWeight: FontWeight.w600,
        letterSpacing: -0.2,
      ),
      titleMedium: const TextStyle(
        fontFamily: _uiFamily,
        fontSize: 15,
        fontWeight: FontWeight.w600,
        letterSpacing: 0,
      ),
      titleSmall: const TextStyle(
        fontFamily: _uiFamily,
        fontSize: 13,
        fontWeight: FontWeight.w500,
      ),
      bodyLarge: const TextStyle(
        fontFamily: _uiFamily,
        fontSize: 14,
        fontWeight: FontWeight.w400,
        height: 1.45,
      ),
      bodyMedium: const TextStyle(
        fontFamily: _uiFamily,
        fontSize: 13,
        fontWeight: FontWeight.w400,
        height: 1.4,
      ),
      bodySmall: const TextStyle(
        fontFamily: _uiFamily,
        fontSize: 12,
        fontWeight: FontWeight.w400,
        height: 1.4,
      ),
      labelMedium: const TextStyle(
        fontFamily: _uiFamily,
        fontSize: 12,
        fontWeight: FontWeight.w500,
      ),
      labelSmall: const TextStyle(
        fontFamily: _uiFamily,
        fontSize: 11,
        fontWeight: FontWeight.w500,
        letterSpacing: 0.2,
      ),
    );
  }

  /// JetBrains Mono for monospaced technical content. Used by
  /// FaiMono helper. Only Regular (400) and Medium (500) weights
  /// are bundled — heavier requests fall back to the nearest.
  static TextStyle mono({
    double size = 12,
    FontWeight weight = FontWeight.w400,
    Color? color,
  }) => TextStyle(
    fontFamily: _monoFamily,
    fontSize: size,
    fontWeight: weight,
    color: color,
    height: 1.4,
  );

  /// Shared markdown style used by every inline doc renderer
  /// (Welcome DocReaderSheet, Store module-detail DocsPanel,
  /// future help surfaces). Centralising it means every doc
  /// reads in the same typography, with the same dark-theme
  /// safe colors.
  ///
  /// Why every property is set explicitly even when it just
  /// re-states the default:
  ///
  /// - `flutter_markdown`'s `MarkdownStyleSheet.fromTheme(theme)`
  ///   sets `code` with `backgroundColor: theme.cardTheme.color`.
  ///   That per-span backgroundColor is rendered behind every
  ///   character inside a fenced code block, which on a dark
  ///   theme shows up as horizontal bands behind ASCII diagrams.
  ///   We force `backgroundColor: Colors.transparent` to suppress
  ///   it — the codeblock's own decoration provides the panel
  ///   background.
  /// - `blockquote` defaults to a hardcoded `Colors.blue.shade100`
  ///   background that is unreadable in dark mode. We replace it
  ///   with theme-aware tokens (surfaceContainer + left accent
  ///   border).
  /// - `tableCellsDecoration` has the same dark-mode bug, fixed
  ///   the same way.
  static MarkdownStyleSheet markdownStyle(ThemeData theme) {
    return MarkdownStyleSheet.fromTheme(theme).copyWith(
      p: theme.textTheme.bodyMedium?.copyWith(height: 1.5),
      code: mono(
        size: 12,
        color: theme.colorScheme.onSurface,
      ).copyWith(backgroundColor: Colors.transparent),
      codeblockDecoration: BoxDecoration(
        color: theme.colorScheme.surfaceContainerHigh,
        borderRadius: BorderRadius.circular(FaiRadius.sm),
        border: Border.all(color: theme.colorScheme.outlineVariant),
      ),
      codeblockPadding: const EdgeInsets.all(FaiSpace.md),
      blockquote: theme.textTheme.bodyMedium?.copyWith(
        color: theme.colorScheme.onSurface,
      ),
      blockquoteDecoration: BoxDecoration(
        color: theme.colorScheme.surfaceContainer,
        borderRadius: BorderRadius.circular(FaiRadius.sm),
        border: Border(
          left: BorderSide(color: theme.colorScheme.primary, width: 3),
        ),
      ),
      blockquotePadding: const EdgeInsets.fromLTRB(
        FaiSpace.md,
        FaiSpace.sm,
        FaiSpace.sm,
        FaiSpace.sm,
      ),
      tableCellsDecoration: BoxDecoration(
        color: theme.colorScheme.surfaceContainerHigh,
      ),
    );
  }

  /// Build a Studio ThemeData around any [ColorScheme]. Used by
  /// the built-in light/dark constructors and by
  /// `themeDataFromScheme` when a Studio plugin supplies the
  /// palette. Single entry-point so the plugin path and the
  /// built-in path always produce structurally identical
  /// ThemeData (same textTheme, same component themes); without
  /// this, swapping between them at runtime trips Flutter's
  /// TextStyle.lerp with mismatched `inherit` values.
  static ThemeData fromColorScheme(ColorScheme scheme) => _build(scheme);

  static ThemeData dark() {
    final scheme = ColorScheme(
      brightness: Brightness.dark,
      primary: FaiColors.accent,
      onPrimary: FaiColors.black,
      primaryContainer: FaiColors.accentMuted,
      onPrimaryContainer: FaiColors.textStrong,
      secondary: FaiColors.accentDeep,
      onSecondary: FaiColors.black,
      secondaryContainer: FaiColors.surfaceHigh,
      onSecondaryContainer: FaiColors.text,
      tertiary: FaiColors.accent,
      onTertiary: FaiColors.black,
      tertiaryContainer: FaiColors.surfaceHigh,
      onTertiaryContainer: FaiColors.text,
      error: FaiColors.danger,
      onError: FaiColors.textStrong,
      errorContainer: const Color(0xFF3F1518),
      onErrorContainer: const Color(0xFFFCA5A5),
      surface: FaiColors.black,
      onSurface: FaiColors.text,
      onSurfaceVariant: FaiColors.muted,
      outline: FaiColors.border,
      outlineVariant: FaiColors.border,
      surfaceContainerLowest: FaiColors.black,
      surfaceContainerLow: FaiColors.surface,
      surfaceContainer: FaiColors.surface,
      surfaceContainerHigh: FaiColors.surfaceHigh,
      surfaceContainerHighest: FaiColors.surfaceHigh,
    );
    return _build(scheme);
  }

  static ThemeData light() {
    final scheme = ColorScheme(
      brightness: Brightness.light,
      primary: FaiColors.accentDeep,
      onPrimary: FaiColors.lightSurface,
      primaryContainer: const Color(0xFFE0F2FE),
      onPrimaryContainer: FaiColors.accentMuted,
      secondary: FaiColors.accentMuted,
      onSecondary: FaiColors.lightSurface,
      secondaryContainer: FaiColors.lightSurfaceHigh,
      onSecondaryContainer: FaiColors.lightText,
      tertiary: FaiColors.accentDeep,
      onTertiary: FaiColors.lightSurface,
      tertiaryContainer: const Color(0xFFE0F2FE),
      onTertiaryContainer: FaiColors.accentMuted,
      error: FaiColors.danger,
      onError: FaiColors.lightSurface,
      errorContainer: const Color(0xFFFEE2E2),
      onErrorContainer: const Color(0xFF7F1D1D),
      surface: FaiColors.lightCanvas,
      onSurface: FaiColors.lightText,
      onSurfaceVariant: FaiColors.lightMuted,
      outline: FaiColors.lightBorder,
      outlineVariant: FaiColors.lightBorder,
      surfaceContainerLowest: FaiColors.lightCanvas,
      surfaceContainerLow: FaiColors.lightSurface,
      surfaceContainer: FaiColors.lightSurface,
      surfaceContainerHigh: FaiColors.lightSurfaceHigh,
      surfaceContainerHighest: FaiColors.lightSurfaceHigh,
    );
    return _build(scheme);
  }

  static ThemeData _build(ColorScheme scheme) {
    final textTheme = _textTheme(scheme.brightness);
    return ThemeData(
      useMaterial3: true,
      colorScheme: scheme,
      scaffoldBackgroundColor: scheme.surface,
      textTheme: textTheme,
      primaryTextTheme: textTheme,
      appBarTheme: AppBarTheme(
        backgroundColor: scheme.surface,
        foregroundColor: scheme.onSurface,
        iconTheme: IconThemeData(color: scheme.onSurface),
        elevation: 0,
        scrolledUnderElevation: 0,
        centerTitle: false,
        // Bake the foreground colour straight into the title
        // style. `headlineSmall` is built from the bundled Inter
        // family with no color slot, and Material's "merge foreground
        // colour at draw time" path leaves it null in some
        // light-mode builds — the title rendered white-on-white.
        titleTextStyle: textTheme.headlineSmall?.copyWith(
          color: scheme.onSurface,
        ),
        toolbarHeight: 64,
      ),
      cardTheme: CardThemeData(
        color: scheme.surfaceContainer,
        elevation: 0,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(FaiRadius.md),
          side: BorderSide(color: scheme.outlineVariant),
        ),
        margin: EdgeInsets.zero,
      ),
      filledButtonTheme: FilledButtonThemeData(
        style: FilledButton.styleFrom(
          backgroundColor: scheme.primary,
          foregroundColor: scheme.onPrimary,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(FaiRadius.sm),
          ),
          padding: const EdgeInsets.symmetric(
            horizontal: FaiSpace.lg,
            vertical: FaiSpace.md,
          ),
          textStyle: textTheme.labelMedium,
          animationDuration: FaiMotion.fast,
        ),
      ),
      outlinedButtonTheme: OutlinedButtonThemeData(
        style: OutlinedButton.styleFrom(
          foregroundColor: scheme.onSurface,
          side: BorderSide(color: scheme.outline),
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(FaiRadius.sm),
          ),
          padding: const EdgeInsets.symmetric(
            horizontal: FaiSpace.lg,
            vertical: FaiSpace.md,
          ),
          textStyle: textTheme.labelMedium,
          animationDuration: FaiMotion.fast,
        ),
      ),
      iconButtonTheme: IconButtonThemeData(
        style: IconButton.styleFrom(foregroundColor: scheme.onSurfaceVariant),
      ),
      navigationRailTheme: NavigationRailThemeData(
        backgroundColor: scheme.surfaceContainerLow,
        indicatorColor: scheme.primaryContainer,
        selectedIconTheme: IconThemeData(color: scheme.primary, size: 22),
        unselectedIconTheme: IconThemeData(
          color: scheme.onSurfaceVariant,
          size: 22,
        ),
        selectedLabelTextStyle: textTheme.labelMedium?.copyWith(
          color: scheme.primary,
        ),
        unselectedLabelTextStyle: textTheme.labelMedium?.copyWith(
          color: scheme.onSurfaceVariant,
        ),
        useIndicator: true,
      ),
      dividerColor: scheme.outlineVariant,
      dividerTheme: DividerThemeData(
        color: scheme.outlineVariant,
        thickness: 1,
        space: 1,
      ),
      snackBarTheme: SnackBarThemeData(
        backgroundColor: scheme.surfaceContainerHigh,
        contentTextStyle: textTheme.bodyMedium?.copyWith(
          color: scheme.onSurface,
        ),
        behavior: SnackBarBehavior.floating,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(FaiRadius.sm),
        ),
      ),
    );
  }
}
