// ignore_for_file: avoid_print, depend_on_referenced_packages

import 'dart:convert';
import 'dart:io';

import 'package:collection/collection.dart';

void main() {
  generatePrimitive();
  generateSemantic();
}

void generatePrimitive() {
  // 1. Load the JSON file.
  final jsonString =
      File('script/Primitive.Mode 1.tokens.json').readAsStringSync();
  final jsonData = jsonDecode(jsonString) as Map<String, dynamic>;

  // 2. Prepare the output code.
  final buffer = StringBuffer();

  buffer.writeln('''
// ignore_for_file: constant_identifier_names, non_constant_identifier_names
//
// AUTO-GENERATED - DO NOT EDIT DIRECTLY
//
// This file is auto-generated by the generate_theme.dart script
// Generation time: ${DateTime.now().toIso8601String()}
//
// To modify these colors, edit the source JSON files and run the script:
//
// dart run script/generate_theme.dart
//
import 'package:flutter/material.dart';

class AppFlowyPrimitiveTokens {
  AppFlowyPrimitiveTokens._();''');

  // 3. Process each color category.
  jsonData.forEach((categoryName, categoryData) {
    categoryData.forEach((tokenName, tokenData) {
      processPrimitiveTokenData(
        buffer,
        tokenData,
        '${categoryName}_$tokenName',
      );
    });
  });

  buffer.writeln('}');

  // 4. Write the output to a Dart file.
  final outputFile = File('lib/src/theme/data/appflowy_default/primitive.dart');
  outputFile.writeAsStringSync(buffer.toString());

  print('Successfully generated ${outputFile.path}');
}

void processPrimitiveTokenData(
  StringBuffer buffer,
  Map<String, dynamic> tokenData,
  final String currentTokenName,
) {
  if (tokenData
      case {
        r'$type': 'color',
        r'$value': final String colorValue,
      }) {
    final dartColorValue = convertColor(colorValue);
    final dartTokenName = currentTokenName.replaceAll('-', '_').toCamelCase();

    buffer.writeln('''

  /// $colorValue
  static Color get $dartTokenName => Color(0x$dartColorValue);''');
  } else {
    tokenData.forEach((key, value) {
      if (value is Map<String, dynamic>) {
        processPrimitiveTokenData(buffer, value, '${currentTokenName}_$key');
      }
    });
  }
}

void generateSemantic() {
  // 1. Load the JSON file.
  final lightJsonString =
      File('script/Semantic.Light Mode.tokens.json').readAsStringSync();
  final darkJsonString =
      File('script/Semantic.Dark Mode.tokens.json').readAsStringSync();
  final lightJsonData = jsonDecode(lightJsonString) as Map<String, dynamic>;
  final darkJsonData = jsonDecode(darkJsonString) as Map<String, dynamic>;

  // 2. Prepare the output code.
  final buffer = StringBuffer();

  buffer.writeln('''
// ignore_for_file: constant_identifier_names, non_constant_identifier_names
//
// AUTO-GENERATED - DO NOT EDIT DIRECTLY
//
// This file is auto-generated by the generate_theme.dart script
// Generation time: ${DateTime.now().toIso8601String()}
//
// To modify these colors, edit the source JSON files and run the script:
//
// dart run script/generate_theme.dart
//
import 'package:appflowy_ui/appflowy_ui.dart';
import 'package:flutter/material.dart';

import '../shared.dart';

class AppFlowyDefaultTheme implements AppFlowyThemeBuilder {''');

  // 3. Process light mode semantic tokens
  buffer.writeln('''
  @override
  AppFlowyThemeData light({
    String? fontFamily,
  }) {
    final textStyle = AppFlowyBaseTextStyle.customFontFamily(fontFamily ?? '');
    final borderRadius = AppFlowySharedTokens.buildBorderRadius();
    final spacing = AppFlowySharedTokens.buildSpacing();
    final shadow = AppFlowySharedTokens.buildShadow(Brightness.light);''');

  lightJsonData.forEach((categoryName, categoryData) {
    if ([
      'Spacing',
      'Border_Radius',
      'Shadow',
      'Badge_Color',
    ].contains(categoryName)) {
      return;
    }

    final fullCategoryName = "${categoryName}_color_scheme".toCamelCase();
    final className = 'AppFlowy${fullCategoryName.toCapitalize()}';

    buffer
      ..writeln()
      ..writeln('    final $fullCategoryName = $className(');

    categoryData.forEach((tokenName, tokenData) {
      processSemanticTokenData(buffer, tokenData, tokenName);
    });
    buffer.writeln('    );');
  });

  buffer.writeln();
  buffer.writeln('''
    return AppFlowyThemeData(
      textStyle: textStyle,
      textColorScheme: textColorScheme,
      borderColorScheme: borderColorScheme,
      fillColorScheme: fillColorScheme,
      surfaceColorScheme: surfaceColorScheme,
      backgroundColorScheme: backgroundColorScheme,
      iconColorScheme: iconColorScheme,
      brandColorScheme: brandColorScheme,
      otherColorsColorScheme: otherColorsColorScheme,
      borderRadius: borderRadius,
      surfaceContainerColorScheme: surfaceContainerColorScheme,
      badgeColorScheme: badgeColorScheme,
      spacing: spacing,
      shadow: shadow,
    );
  }''');

  buffer.writeln();

  buffer.writeln('''
  @override
  AppFlowyThemeData dark({
    String? fontFamily,
  }) {
    final textStyle = AppFlowyBaseTextStyle.customFontFamily(fontFamily ?? '');
    final borderRadius = AppFlowySharedTokens.buildBorderRadius();
    final spacing = AppFlowySharedTokens.buildSpacing();
    final shadow = AppFlowySharedTokens.buildShadow(Brightness.dark);''');

  darkJsonData.forEach((categoryName, categoryData) {
    if ([
      'Spacing',
      'Border_Radius',
      'Shadow',
      'Badge_Color',
    ].contains(categoryName)) {
      return;
    }

    final fullCategoryName = "${categoryName}_color_scheme".toCamelCase();
    final className = 'AppFlowy${fullCategoryName.toCapitalize()}';

    buffer
      ..writeln()
      ..writeln('    final $fullCategoryName = $className(');

    categoryData.forEach((tokenName, tokenData) {
      if (tokenData is Map<String, dynamic>) {
        processSemanticTokenData(buffer, tokenData, tokenName);
      }
    });
    buffer.writeln('    );');
  });

  buffer.writeln();

  buffer.writeln('''
    return AppFlowyThemeData(
      textStyle: textStyle,
      textColorScheme: textColorScheme,
      borderColorScheme: borderColorScheme,
      fillColorScheme: fillColorScheme,
      surfaceColorScheme: surfaceColorScheme,
      backgroundColorScheme: backgroundColorScheme,
      iconColorScheme: iconColorScheme,
      brandColorScheme: brandColorScheme,
      otherColorsColorScheme: otherColorsColorScheme,
      borderRadius: borderRadius,
      surfaceContainerColorScheme: surfaceContainerColorScheme,
      badgeColorScheme: badgeColorScheme,
      spacing: spacing,
      shadow: shadow,
    );
  }''');

  buffer.writeln('}');

  // 4. Write the output to a Dart file.
  final outputFile = File('lib/src/theme/data/appflowy_default/semantic.dart');
  outputFile.writeAsStringSync(buffer.toString());

  print('Successfully generated ${outputFile.path}');
}

void processSemanticTokenData(
  StringBuffer buffer,
  Map<String, dynamic> json,
  final String currentTokenName,
) {
  if (json
      case {
        r'$type': 'color',
        r'$value': final String value,
      }) {
    final semanticTokenName =
        currentTokenName.replaceAll('-', '_').toCamelCase();

    final String colorValueOrPrimitiveToken;
    if (value.isColor) {
      colorValueOrPrimitiveToken = 'Color(0x${convertColor(value)})';
    } else {
      final primitiveToken = value
          .replaceAll(RegExp(r'\{|\}'), '')
          .replaceAll(RegExp(r'\.|-'), '_')
          .toCamelCase();
      colorValueOrPrimitiveToken = 'AppFlowyPrimitiveTokens.$primitiveToken';
    }

    buffer.writeln('      $semanticTokenName: $colorValueOrPrimitiveToken,');
  } else {
    json.forEach((key, value) {
      if (value is Map<String, dynamic>) {
        processSemanticTokenData(
          buffer,
          value,
          '${currentTokenName}_$key',
        );
      }
    });
  }
}

String convertColor(String hexColor) {
  String color = hexColor.toUpperCase().replaceAll('#', '');
  if (color.length == 6) {
    color = 'FF$color'; // Add missing alpha channel
  } else if (color.length == 8) {
    color = color.substring(6) + color.substring(0, 6); // Rearrange to ARGB
  }
  return color;
}

extension on String {
  String toCamelCase() {
    return split('_').mapIndexed((index, part) {
      if (index == 0) {
        return part.toLowerCase();
      } else {
        return part[0].toUpperCase() + part.substring(1).toLowerCase();
      }
    }).join();
  }

  String toCapitalize() {
    if (isEmpty) {
      return this;
    }
    return '${this[0].toUpperCase()}${substring(1)}';
  }

  bool get isColor =>
      startsWith('#') ||
      (startsWith('0x') && length == 10) ||
      (startsWith('0xFF') && length == 12);
}
