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

import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_file_dialog/flutter_file_dialog.dart';
import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';

import '../controls/padding.dart';
import '../controls/responsive_max_width.dart';
import '../controls/standard_appbar.dart';
import '../services/log_service.dart';
import '../services/setting_service.dart';
import '../utils/clipboard_utils.dart';
import '../utils/dateutils.dart';
import '../utils/json_printer.dart';
import '../utils/logrecord_extensions.dart';
import '../utils/snackbar_builder.dart';

final _logger = Logger('LogViewerScreen');

class LogViewerScreen extends StatefulWidget {
  const LogViewerScreen({super.key});

  @override
  State<LogViewerScreen> createState() => _LogViewerScreenState();
}

class _LogViewerScreenState extends State<LogViewerScreen> {
  var filterText = '';
  var filterByText = false;
  var filterByModule = false;
  var filterModuleName = '';
  var attemptingWrite = false;

  Future<void> _writeEventsLog(List<LogRecord> events) async {
    if (attemptingWrite) {
      return;
    }

    attemptingWrite = true;
    try {
      final json =
          PrettyJsonEncoder().convert(events.map((e) => e.toJson()).toList());
      final filename = 'EventsLog_${DateTime.now().toFileNameString()}.json';
      if (Platform.isAndroid || Platform.isIOS) {
        final params = SaveFileDialogParams(
          data: Uint8List.fromList(json.codeUnits),
          fileName: filename,
        );
        await FlutterFileDialog.saveFile(params: params);
        if (mounted) {
          buildSnackbar(context, 'Wrote Events Log to: $filename');
        }
      } else {
        final appsDir = await getApplicationDocumentsDirectory();
        final location = await FilePicker.platform.saveFile(
          dialogTitle: 'Save Events Log',
          fileName: filename,
          initialDirectory: appsDir.path,
        );
        if (location != null) {
          await File(location).writeAsString(json);
          if (mounted) {
            buildSnackbar(context, 'Wrote Events Log to: $location');
          }
        }
      }
    } catch (e) {
      _logger.severe('Error attempting to write out log: $e');
      if (mounted) {
        buildSnackbar(context, 'Error attempting to write out log: $e');
      }
    }
    attemptingWrite = false;
  }

  @override
  Widget build(BuildContext context) {
    final settings = context.watch<SettingsService>();
    final logService = context.watch<LogService>();
    final events = logService.events;

    return Scaffold(
      appBar: StandardAppBar.build(context, 'Log Viewer'),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: ResponsiveMaxWidth(
            child: Column(
              children: [
                buildLogPanel(settings),
                buildModuleFilter(settings, events),
                buildTextSearchPanel(),
                const VerticalPadding(),
                buildLogList(settings.logLevel, events),
              ],
            ),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton.small(
        onPressed: () => _writeEventsLog(events),
        child: const Icon(Icons.save),
      ),
    );
  }

  Widget buildLogPanel(SettingsService settings) {
    return ListTile(
      title: const Text('Log Level'),
      trailing: DropdownButton<Level>(
          value: settings.logLevel,
          items: Level.LEVELS
              .map((c) => DropdownMenuItem(value: c, child: Text(c.name)))
              .toList(),
          onChanged: (value) {
            settings.logLevel = value ?? Level.OFF;
          }),
    );
  }

  Widget buildModuleFilter(SettingsService settings, List<LogRecord> events) {
    final modules = events.map((e) => e.loggerName).toSet().toList();
    modules.sort();
    modules.add('');
    return ListTile(
      leading: Checkbox(
        value: filterByModule,
        onChanged: (bool? value) {
          setState(() {
            filterByModule = value ?? false;
          });
        },
      ),
      title: filterByModule ? null : const Text('Filter by module'),
      trailing: !filterByModule
          ? null
          : DropdownButton<String>(
              value: filterModuleName,
              items: modules
                  .map((c) => DropdownMenuItem(value: c, child: Text(c)))
                  .toList(),
              onChanged: (value) {
                setState(() {
                  filterModuleName = value ?? '';
                });
              }),
    );
  }

  Widget buildTextSearchPanel() {
    return ListTile(
      leading: Checkbox(
        value: filterByText,
        onChanged: (value) {
          setState(() {
            filterByText = value ?? false;
          });
        },
      ),
      title: TextField(
        enabled: filterByText,
        onChanged: (value) {
          setState(() {
            filterText = value.toLowerCase();
          });
        },
        decoration: InputDecoration(
          labelText: 'Filter by text',
          alignLabelWithHint: true,
          border: OutlineInputBorder(
            borderSide: BorderSide(
              color: Theme.of(context).highlightColor,
            ),
            borderRadius: BorderRadius.circular(5.0),
          ),
        ),
      ),
    );
  }

  Widget buildLogList(Level logLevel, List<LogRecord> allEvents) {
    final events = allEvents.where((e) {
      final levelFilterPasses = e.level >= logLevel;
      final passesTextFilter = filterByText
          ? e.message.toLowerCase().contains(filterText.toLowerCase())
          : true;
      final passesModuleFilter = filterByModule
          ? filterModuleName.isEmpty || e.loggerName == filterModuleName
          : true;

      return levelFilterPasses && passesTextFilter && passesModuleFilter;
    }).toList();
    return Expanded(
      child: ListView.separated(
        itemBuilder: (context, index) {
          final event = events[index];
          return ListTile(
            onLongPress: () {
              copyToClipboard(
                context: context,
                text: jsonEncode(event.toJson()),
              );
            },
            titleAlignment: ListTileTitleAlignment.titleHeight,
            leading: Text(event.level.toString()),
            title:
                Text('${event.loggerName} at ${event.time.toIso8601String()}'),
            subtitle: Text(
              event.message,
              softWrap: true,
            ),
          );
        },
        separatorBuilder: (_, __) => const Divider(),
        itemCount: events.length,
      ),
    );
  }
}
