import 'dart:io';
import 'package:mason/mason.dart';
import 'package:yaml/yaml.dart';

void run(HookContext context) async {
  final logger = context.logger;

  final String pathUrl = context.vars['path_url'] ?? '';
  final String functionName = context.vars['function_name'] ?? '';
  final String requestType = (context.vars['request_type'] as String).toLowerCase();
  final String requestBody = context.vars['request_body'] as String;

  if (requestType == 'post' && requestBody.isEmpty) {
    logger.err('❌ Error: POST requests require a Request Body type.');
    return;
  }

  final cName = functionName.camelCase;
  final bool hasData = requestBody.isNotEmpty;
  final String paramType = hasData ? requestBody : 'dynamic';

  // Auto-Detect Project Name
  String projectName = 'your_project';
  final pubspecFile = File('pubspec.yaml');
  if (pubspecFile.existsSync()) {
    final doc = loadYaml(pubspecFile.readAsStringSync());
    projectName = doc['name'] ?? projectName;
  }

  final progress = logger.progress('🚀 Injecting logic...');

  final filesToUpdate = [
    'lib/Grounded/comms/CommsDirections.dart',
    'lib/Grounded/comms/Comms.dart',
    'lib/Grounded/comms/ConnectComms.dart',
    'lib/Grounded/informatics/AppDataManager.dart'
  ];

  // Find Import Path and Filename
  String? importLine;
  String? importFileName;
  if (hasData && !['String','int','double','bool'].contains(requestBody)) {
    final result = await _findClassData(requestBody, projectName);
    importLine = result?['line'];
    importFileName = result?['fileName'];
  }

  final String sharedParams = hasData ? '$paramType request' : '';
  final String callParams = hasData ? 'request' : '';

  // 1. CommsDirections
  await _inject(filesToUpdate[0], "String ${cName}Path = '$pathUrl';", logger,
      isClass: false, duplicateCheck: "${cName}Path");

  // 2. comms.dart
  final String dioData = requestType == 'post' ? 'data: request' : 'options: Options(headers: dio.options.headers)';
  final String commsMethod = '''
  @override
  Future<Response> $cName($sharedParams) async {
    Pair navigation = await getRequestHeaders(${cName}Path, "");
    dio.options.headers = navigation.value;
    return await dio.$requestType(
      navigation.key, 
      $dioData
    );
  }''';
  await _inject(filesToUpdate[1], commsMethod, logger,
      importLine: importLine, importFileName: importFileName, duplicateCheck: "Future<Response> $cName");

  // 3. ConnectComms.dart
  await _inject(filesToUpdate[2], "  Future<Response> $cName($sharedParams);", logger,
      importLine: importLine, importFileName: importFileName, duplicateCheck: "Future<Response> $cName");

  // 4. AppDataManager.dart
  final String dataManagerMethod = '''
  @override
  Future<Response> $cName($sharedParams) async {
    return await connectComms.$cName($callParams);
  }''';
  await _inject(filesToUpdate[3], dataManagerMethod, logger,
      importLine: importLine, importFileName: importFileName, duplicateCheck: "Future<Response> $cName");

  progress.complete('✨ Injection & Deduplication complete.');

  final shouldFormat = logger.confirm('🎨 Run "dart format" on updated files?', defaultValue: false);
  if (shouldFormat) {
    for (final path in filesToUpdate) {
      await Process.run('dart', ['format', path]);
    }
    logger.success('✅ Files formatted.');
  }
}

/// Returns both the full import line and just the filename (e.g. user_request.dart)
Future<Map<String, String>?> _findClassData(String className, String projectName) async {
  final directory = Directory('lib');
  if (!directory.existsSync()) return null;
  final List<FileSystemEntity> files = directory.listSync(recursive: true);
  for (var file in files) {
    if (file is File && file.path.endsWith('.dart')) {
      final content = await file.readAsString();
      if (content.contains('class $className') || content.contains('enum $className')) {
        final fileName = file.path.split('/').last;
        final packagePath = file.path.replaceFirst('lib/Grounded/', '');
        return {
          'line': "import 'package:$projectName/$packagePath';",
          'fileName': fileName,
        };
      }
    }
  }
  return null;
}

Future<void> _inject(String path, String content, Logger logger,
    {bool isClass = true, String? importLine, String? importFileName, String? duplicateCheck}) async {
  final file = File(path);
  if (!await file.exists()) return;
  String fileContent = await file.readAsString();

  // 1. Prevent Duplicate Method/Variable
  if (duplicateCheck != null && fileContent.contains(duplicateCheck)) {
    return; // Silently skip methods that exist
  }

  // 2. Smart Import Check: Look for the filename inside any import statement
  if (importLine != null && importFileName != null) {
    // Regex matches: import 'any/path/filename.dart'; or "any/path/filename.dart";
    final importRegex = RegExp("import ['\"].*?$importFileName['\"];");
    if (!importRegex.hasMatch(fileContent)) {
      fileContent = "$importLine\n$fileContent";
    }
  }

  if (isClass) {
    final lastBrace = fileContent.lastIndexOf('}');
    if (lastBrace != -1) {
      fileContent = fileContent.substring(0, lastBrace).trimRight() + '\n\n$content\n' + fileContent.substring(lastBrace);
    } else {
      fileContent += '\n\n$content';
    }
  } else {
    fileContent = fileContent.trimRight() + '\n\n$content\n';
  }
  await file.writeAsString(fileContent);
}