import 'dart:ui';

import 'package:flex_seed_scheme/flex_seed_scheme.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:nc_photos/controller/pref_controller.dart';
import 'package:nc_photos/theme/dimension.dart';
import 'package:np_ui/np_ui.dart';

const defaultSeedColor = Color(0xFF2196F3);

extension ThemeExtension on ThemeData {
  double get widthLimitedContentMaxWidth => 550.0;

  Color get listPlaceholderBackgroundColor =>
      colorScheme.primaryContainer.withOpacity(.6);

  Color get listPlaceholderForegroundColor =>
      colorScheme.onPrimaryContainer.withOpacity(.7);

  Color get homeNavigationBarBackgroundColor =>
      elevate(colorScheme.surface, 2).withOpacity(.55);

  Color get onDarkSurface {
    return brightness == Brightness.light
        ? colorScheme.onInverseSurface
        : colorScheme.onSurface;
  }

  ImageFilter get appBarBlurFilter => ImageFilter.blur(
        sigmaX: 12,
        sigmaY: 12,
        tileMode: TileMode.mirror,
      );

  Color get nextcloudBlue => const Color(0xFF0082C9);

  LinearGradient get photoGridShimmerGradient {
    final Color color;
    if (brightness == Brightness.light) {
      color = Colors.white.withOpacity(.85);
    } else {
      color = Colors.white.withOpacity(.25);
    }
    return LinearGradient(
      colors: [
        listPlaceholderBackgroundColor.withOpacity(0),
        color,
        listPlaceholderBackgroundColor.withOpacity(0),
      ],
      stops: const [0.1, 0.3, 0.4],
      begin: const Alignment(-1.0, -0.3),
      end: const Alignment(1.0, 0.3),
      tileMode: TileMode.clamp,
    );
  }

  /// Apply surface tint to [color] based on the [elevation] level
  ///
  /// This function is a temporary workaround for widgets not yet fully
  /// supported Material 3
  Color elevate(Color color, int elevation) {
    final double tintOpacity;
    switch (elevation) {
      case 1:
        tintOpacity = 0.05;
        break;
      case 2:
        tintOpacity = 0.08;
        break;
      case 3:
        tintOpacity = 0.11;
        break;
      case 4:
        tintOpacity = 0.12;
        break;
      case 5:
      default:
        tintOpacity = 0.14;
        break;
    }
    return Color.lerp(color, colorScheme.surfaceTint, tintOpacity)!;
  }
}

class DarkModeSwitchTheme extends StatelessWidget {
  const DarkModeSwitchTheme({
    super.key,
    required this.child,
  });

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Theme(
      data: theme.copyWith(
        switchTheme: SwitchThemeData(
          trackColor: MaterialStateProperty.all(theme.colorScheme.surface),
          thumbColor: MaterialStateProperty.all(Colors.black87),
        ),
        colorScheme: theme.colorScheme.copyWith(
          outline: Colors.transparent,
        ),
      ),
      child: child,
    );
  }

  final Widget child;
}

ThemeData buildTheme(BuildContext context, Brightness brightness) {
  return (brightness == Brightness.light)
      ? buildLightTheme(context)
      : buildDarkTheme(context);
}

ThemeData buildLightTheme(BuildContext context, [ColorScheme? dynamicScheme]) {
  final colorScheme = _getColorScheme(context, dynamicScheme, Brightness.light);
  return _applyColorScheme(colorScheme);
}

ThemeData buildDarkTheme(BuildContext context, [ColorScheme? dynamicScheme]) {
  final colorScheme = _getColorScheme(context, dynamicScheme, Brightness.dark);
  if (context.read<PrefController>().isUseBlackInDarkTheme.value) {
    return _applyColorScheme(colorScheme.copyWith(
      background: Colors.black,
      surface: Colors.grey[900],
    ));
  } else {
    return _applyColorScheme(colorScheme);
  }
}

ColorScheme _getColorScheme(
    BuildContext context, ColorScheme? dynamicScheme, Brightness brightness) {
  var primary = context.read<PrefController>().seedColorValue;
  Color? secondary;
  if (primary == null) {
    if (dynamicScheme != null) {
      return dynamicScheme;
    } else {
      primary = defaultSeedColor;
    }
  } else {
    secondary = context.read<PrefController>().secondarySeedColorValue;
  }
  return SeedColorScheme.fromSeeds(
    brightness: brightness,
    tones: FlexTones.oneHue(brightness),
    primaryKey: primary,
    secondaryKey: secondary,
  );
}

ThemeData _applyColorScheme(ColorScheme colorScheme) {
  return ThemeData(
    useMaterial3: true,
    brightness: colorScheme.brightness,
    colorScheme: colorScheme,
    scaffoldBackgroundColor: colorScheme.background,
    appBarTheme: AppBarTheme(
      backgroundColor: colorScheme.background,
      foregroundColor: colorScheme.onSurface,
    ),
    listTileTheme: ListTileThemeData(
      iconColor: colorScheme.onSurfaceVariant,
    ),
    iconTheme: IconThemeData(
      color: colorScheme.onSurfaceVariant,
    ),
    // remove after dialog supports m3
    dialogBackgroundColor:
        Color.lerp(colorScheme.surface, colorScheme.surfaceTint, 0.11),
    popupMenuTheme: PopupMenuThemeData(
      // remove after menu supports m3
      color: Color.lerp(colorScheme.surface, colorScheme.surfaceTint, 0.08),
    ),
    navigationBarTheme: const NavigationBarThemeData(
      // default for Material 3
      height: 80,
    ),
    // remove after checkbox supports m3
    // see: https://m3.material.io/components/checkbox/specs
    checkboxTheme: CheckboxThemeData(
      fillColor: MaterialStateProperty.resolveWith((states) {
        if (states.contains(MaterialState.disabled)) {
          return colorScheme.onSurface;
        } else {
          if (states.contains(MaterialState.selected)) {
            return colorScheme.secondary;
          } else {
            return colorScheme.onSurfaceVariant;
          }
        }
      }),
      checkColor: MaterialStateProperty.all(colorScheme.onPrimary),
    ),
    // remove after checkbox supports m3
    // see: https://m3.material.io/components/switch/specs
    // the color here is slightly modified to work better with the M2 switch
    switchTheme: SwitchThemeData(
      trackColor: MaterialStateProperty.resolveWith((states) {
        if (states.contains(MaterialState.disabled)) {
          if (states.contains(MaterialState.selected)) {
            return colorScheme.onSurface.withOpacity(.12);
          } else {
            return colorScheme.surfaceVariant.withOpacity(.12);
          }
        } else {
          if (states.contains(MaterialState.selected)) {
            // return colorScheme.primary;
            return colorScheme.secondary;
          } else {
            return colorScheme.surfaceVariant;
          }
        }
      }),
      thumbColor: MaterialStateProperty.resolveWith((states) {
        if (states.contains(MaterialState.disabled)) {
          if (states.contains(MaterialState.selected)) {
            // return colorScheme.surface;
            return colorScheme.onSurface.withOpacity(.38);
          } else {
            return colorScheme.onSurface.withOpacity(.38);
          }
        } else {
          if (states.contains(MaterialState.selected)) {
            // return colorScheme.onPrimary;
            return colorScheme.onSecondary;
          } else {
            return colorScheme.outline;
          }
        }
      }),
    ),
    snackBarTheme: SnackBarThemeData(
      backgroundColor: colorScheme.inverseSurface,
      contentTextStyle: TextStyle(
        color: colorScheme.onInverseSurface,
      ),
      actionTextColor: colorScheme.inversePrimary,
      behavior: SnackBarBehavior.floating,
    ),
    sliderTheme: SliderThemeData(
      activeTrackColor: colorScheme.secondary,
      inactiveTrackColor: colorScheme.secondaryContainer,
      thumbColor: colorScheme.secondary,
    ),
    elevatedButtonTheme: ElevatedButtonThemeData(
      style: ButtonStyle(
        backgroundColor:
            MaterialStateProperty.all(colorScheme.secondaryContainer),
        foregroundColor: MaterialStateProperty.all(colorScheme.secondary),
        overlayColor:
            MaterialStateProperty.all(colorScheme.secondary.withOpacity(.1)),
      ),
    ),
    textButtonTheme: TextButtonThemeData(
      style: ButtonStyle(
        foregroundColor: MaterialStateProperty.all(colorScheme.secondary),
        overlayColor:
            MaterialStateProperty.all(colorScheme.secondary.withOpacity(.1)),
      ),
    ),
    textSelectionTheme: TextSelectionThemeData(
      cursorColor: colorScheme.secondary,
      selectionHandleColor: colorScheme.secondary,
      selectionColor: colorScheme.secondary.withOpacity(.4),
    ),
    inputDecorationTheme: InputDecorationTheme(
      focusedBorder: UnderlineInputBorder(
        borderSide: BorderSide(color: colorScheme.secondary, width: 2),
      ),
    ),
    chipTheme: ChipThemeData(
      selectedColor: Color.lerp(
          colorScheme.secondaryContainer, colorScheme.surfaceTint, .14),
      iconTheme: IconThemeData(
        color: colorScheme.secondary,
      ),
    ),
    progressIndicatorTheme:
        ProgressIndicatorThemeData(color: colorScheme.secondary),
    extensions: [
      M3(
        checkbox: M3Checkbox(
          disabled: M3CheckboxDisabled(
            container: colorScheme.onSurface.withOpacity(.38),
          ),
        ),
        filterChip: M3FilterChip(
          disabled: M3FilterChipDisabled(
            containerSelected: colorScheme.onSurface.withOpacity(.12),
            labelText: colorScheme.onSurface.withOpacity(.38),
          ),
        ),
        listTile: M3ListTile(
          enabled: M3ListTileEnabled(
            headline: colorScheme.onSurface,
            supportingText: colorScheme.onSurfaceVariant,
          ),
        ),
      ),
      const AppDimension(
        homeBottomAppBarHeight: 68,
      ),
    ],
  );
}

extension BrightnessExtension on Brightness {
  Brightness invert() {
    switch (this) {
      case Brightness.dark:
        return Brightness.light;
      case Brightness.light:
        return Brightness.dark;
    }
  }
}