import 'package:fast_log_viewer/src/settings/settings_controller.dart';
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:desktop_drop/desktop_drop.dart';

class LogViewerApp extends StatefulWidget {
  final SettingsController settingsController;
  final File? file;
  const LogViewerApp(
      {super.key, required this.settingsController, required this.file});

  @override
  LogViewerAppState createState() => LogViewerAppState();
}

class LogViewerAppState extends State<LogViewerApp> {
  late SettingsController settingsController;
  late File? file;

  @override
  void initState() {
    super.initState();
    settingsController = widget.settingsController;
    file = widget.file;
  }

  @override
  Widget build(BuildContext context) {
    return ListenableBuilder(
        listenable: settingsController,
        builder: (BuildContext context, Widget? child) {
          return MaterialApp(
            debugShowCheckedModeBanner: false,
            title: 'Fast Log Viewer',
            theme: ThemeData(
              primarySwatch: Colors.blue,
              brightness: Brightness.light,
            ),
            darkTheme: ThemeData(
              primarySwatch: Colors.blue,
              brightness: Brightness.dark,
            ),
            themeMode: settingsController.themeMode,
            home: LogViewerScreen(
              file: file,
              onThemeChanged: (ThemeMode mode) async {
                await settingsController.updateThemeMode(mode);
              },
              settingsController: settingsController,
            ),
          );
        });
  }
}

class LogViewerScreen extends StatefulWidget {
  final Function(ThemeMode) onThemeChanged;
  final SettingsController settingsController;
  final File? file;

  const LogViewerScreen(
      {super.key,
      required this.onThemeChanged,
      required this.settingsController,
      required this.file});

  @override
  LogViewerScreenState createState() => LogViewerScreenState();
}

class LogViewerScreenState extends State<LogViewerScreen> {
  List<LogEntry> logs = [];
  List<LogEntry> visibleLogs = [];
  LogLevel selectedLogLevel = LogLevel.info;
  bool _isDragging = false;

  TextEditingController controller = TextEditingController(text: '');

  late SettingsController settingsController;

  @override
  void initState() {
    super.initState();
    if (widget.file != null) {
      widget.file?.readAsString().then((String fileContent) {
        logs = _parseLogFile(fileContent);
        _filterLogs();
      });
    }
    settingsController = widget.settingsController;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          const SizedBox(width: 8),
          Expanded(
            child: TextFormField(
              controller: controller,
              decoration: const InputDecoration(
                labelText: 'Search Logs',
                border: OutlineInputBorder(),
              ),
              onChanged: (value) {
                setState(() {
                  _filterLogs();
                });
              },
            ),
          ),
          Checkbox(
            value: settingsController.useRegex,
            onChanged: (value) {
              settingsController.updateUseRegex(value ?? false);
              _filterLogs();
            },
          ),
          const Text('Use Regex'),
          Checkbox(
            value: settingsController.caseSensitive,
            onChanged: (value) {
              settingsController.updateCaseSensitive(value ?? false);
              _filterLogs();
            },
          ),
          const Text('Case Sensitive'),
          DropdownButton<LogLevel>(
            underline: Container(height: 0),
            alignment: Alignment.center,
            value: selectedLogLevel,
            onChanged: (LogLevel? newValue) {
              if (newValue != null) {
                setState(() {
                  selectedLogLevel = newValue;
                  _filterLogs();
                });
              }
            },
            items: LogLevel.values
                .map<DropdownMenuItem<LogLevel>>((LogLevel level) {
              return DropdownMenuItem<LogLevel>(
                value: level,
                child: Text(level.name),
              );
            }).toList(),
          ),
          IconButton(
            icon: const Icon(Icons.folder_open),
            onPressed: _openFile,
          ),
          DropdownButton<ThemeMode>(
            underline: Container(height: 0),
            alignment: Alignment.center,
            value: settingsController.themeMode,
            onChanged: (ThemeMode? newValue) async {
              if (newValue != null) {
                await settingsController.updateThemeMode(newValue);
                setState(() {
                  widget.onThemeChanged(newValue);
                });
              }
            },
            items: const [
              DropdownMenuItem(
                value: ThemeMode.light,
                child: Text('Light'),
              ),
              DropdownMenuItem(
                value: ThemeMode.dark,
                child: Text('Dark'),
              ),
              DropdownMenuItem(
                value: ThemeMode.system,
                child: Text('System'),
              ),
            ],
          ),
          const SizedBox(width: 8),
        ],
      ),
      body: DropTarget(
        onDragEntered: (details) {
          setState(() {
            _isDragging = true;
          });
        },
        onDragExited: (details) {
          setState(() {
            _isDragging = false;
          });
        },
        onDragDone: (details) async {
          if (details.files.isNotEmpty) {
            var bytes = await details.files.first.readAsBytes();
            setState(() {
              logs = _parseLogFile(String.fromCharCodes(bytes));
              controller.clear();
              _filterLogs();
              _isDragging = false;
            });
          }
        },
        child: Container(
          color: _isDragging
              ? Colors.grey[200]
              : Theme.of(context).scaffoldBackgroundColor,
          child: Column(
            children: [
              Expanded(
                child: SelectionArea(
                    child: ListView.builder(
                  itemCount: visibleLogs.length,
                  itemBuilder: (context, index) {
                    return GestureDetector(
                      child: LogEntryWidget(
                        log: visibleLogs[index],
                      ),
                    );
                  },
                )),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Future<void> _openFile() async {
    FilePickerResult? result =
        await FilePicker.platform.pickFiles(withData: true);
    if (result != null) {
      if (result.files.isNotEmpty) {
        var bytes = result.files.first.bytes;
        if (bytes != null) {
          setState(() {
            logs = _parseLogFile(String.fromCharCodes(bytes));
            controller.clear();
            _filterLogs();
          });
        }
      }
    }
  }

  void _filterLogs() {
    setState(() {
      visibleLogs = logs
          .where((log) =>
              log.level.index >= selectedLogLevel.index &&
              log.contains(controller.text, settingsController.useRegex,
                  caseSensitive: settingsController.caseSensitive))
          .toList();
    });
  }

  List<LogEntry> _parseLogFile(String content) {
    List<LogEntry> parsedLogs = [];
    List<String> lines = content.split('\n');
    for (String line in lines) {
      if (line.isNotEmpty) {
        List<String> parts = line.split('\t');
        if (parts.length >= 5) {
          parsedLogs.add(LogEntry(
            parts[0],
            fromString(parts[1]),
            parts[2],
            parts[3],
            parts[4],
            parts.sublist(5),
          ));
        } else if (!line.contains('*** NEW LOG FILE ***')) {
          parsedLogs.last.message.add(line);
        }
      }
    }
    return parsedLogs;
  }
}

class Component {
  final String name;
  final String address;

  Component(this.name, this.address);
}

enum LogLevel {
  trace,
  debug,
  info,
  warning,
  error,
  unknown,
}

extension LogLevelExtension on LogLevel {
  String get name {
    switch (this) {
      case LogLevel.trace:
        return 'TRACE';
      case LogLevel.debug:
        return 'DEBUG';
      case LogLevel.info:
        return 'INFO';
      case LogLevel.warning:
        return 'WARNING';
      case LogLevel.error:
        return 'ERROR';
      case LogLevel.unknown:
        return 'UNKNOWN';
    }
  }

  String get lowerCaseName => name.toLowerCase();
}

LogLevel fromString(String value) {
  switch (value.toUpperCase()) {
    case 'TRACE':
      return LogLevel.trace;
    case 'DEBUG':
      return LogLevel.debug;
    case 'INFO':
      return LogLevel.info;
    case 'WARNING':
      return LogLevel.warning;
    case 'WARN':
      return LogLevel.warning;
    case 'ERROR':
      return LogLevel.error;
    default:
      return LogLevel.unknown;
  }
}

class LogEntry {
  final String timestamp;
  final LogLevel level;
  final String category;
  final String threadId;
  final String component;
  final List<String> message;

  LogEntry(this.timestamp, this.level, this.category, this.threadId,
      this.component, this.message);

  bool contains(String value, bool useRegex, {bool caseSensitive = false}) {
    if (useRegex) {
      try {
        var regex =
            RegExp(value, caseSensitive: caseSensitive, multiLine: true);

        return regex.hasMatch(timestamp) ||
            regex.hasMatch(level.lowerCaseName) ||
            regex.hasMatch(category) ||
            regex.hasMatch(threadId) ||
            regex.hasMatch(component) ||
            regex.hasMatch(message.join('\n'));
      } catch (e) {
        return false;
      }
    } else if (caseSensitive) {
      var v = value;
      return timestamp.contains(v) ||
          level.name.contains(v) ||
          category.contains(v) ||
          threadId.contains(v) ||
          component.contains(v) ||
          message.where((value) => value.contains(v)).isNotEmpty;
    } else {
      var v = value.toLowerCase();
      return timestamp.toLowerCase().contains(v) ||
          level.lowerCaseName.contains(v) ||
          category.toLowerCase().contains(v) ||
          threadId.toLowerCase().contains(v) ||
          component.toLowerCase().contains(v) ||
          message.where((value) => value.toLowerCase().contains(v)).isNotEmpty;
    }
  }
}

class LogEntryWidget extends StatelessWidget {
  final LogEntry log;

  const LogEntryWidget({super.key, required this.log});

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0),
      child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              '${log.timestamp} - [${log.level.name}] - ${log.category} - ${log.threadId}',
              style: TextStyle(
                fontWeight: FontWeight.bold,
                color: _getLevelColor(log.level),
              ),
            ),
            Text(
              'Component: ${log.component}',
              style: const TextStyle(
                fontStyle: FontStyle.italic,
              ),
            ),
            Text(
              log.message.join('\n'),
            ),
          ],
        ),
      ),
    );
  }

  Color _getLevelColor(LogLevel level) {
    switch (level) {
      case LogLevel.warning:
        return Colors.orange;
      case LogLevel.error:
        return Colors.red;
      case LogLevel.debug:
        return Colors.green;
      default:
        return Colors.blueGrey;
    }
  }
}
