import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:intl/intl.dart';
import 'package:simplecloudnotifier/api/api_client.dart';
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
import 'package:simplecloudnotifier/models/keytoken.dart';
import 'package:simplecloudnotifier/models/user.dart';
import 'package:simplecloudnotifier/pages/filtered_message_view/filtered_message_view.dart';
import 'package:simplecloudnotifier/state/app_auth.dart';
import 'package:simplecloudnotifier/state/app_bar_state.dart';
import 'package:simplecloudnotifier/state/application_log.dart';
import 'package:simplecloudnotifier/types/immediate_future.dart';
import 'package:simplecloudnotifier/utils/navi.dart';
import 'package:simplecloudnotifier/utils/toaster.dart';
import 'package:simplecloudnotifier/utils/ui.dart';
import 'package:provider/provider.dart';

class KeyTokenViewPage extends StatefulWidget {
  const KeyTokenViewPage({
    required this.keytokenID,
    required this.preloadedData,
    required this.needsReload,
    super.key,
  });

  final String keytokenID;
  final KeyToken? preloadedData;

  final void Function()? needsReload;

  @override
  State<KeyTokenViewPage> createState() => _KeyTokenViewPageState();
}

enum EditState { none, editing, saving }

enum KeyTokenViewPageInitState { loading, okay, error }

class _KeyTokenViewPageState extends State<KeyTokenViewPage> {
  static final _dateFormat = DateFormat('yyyy-MM-dd HH:mm'); //TODO setting

  late ImmediateFuture<UserPreview> _futureOwner;

  final TextEditingController _ctrlName = TextEditingController();

  int _loadingIndeterminateCounter = 0;

  EditState _editName = EditState.none;
  String? _nameOverride = null;

  KeyTokenPreview? keytokenPreview;
  KeyToken? keytoken;

  KeyTokenViewPageInitState loadingState = KeyTokenViewPageInitState.loading;
  String errorMessage = '';

  @override
  void initState() {
    _initStateAsync(true);

    super.initState();
  }

  Future<void> _initStateAsync(bool usePreload) async {
    final userAcc = Provider.of<AppAuth>(context, listen: false);

    if (widget.preloadedData != null && usePreload) {
      keytoken = widget.preloadedData!;
      keytokenPreview = widget.preloadedData!.toPreview();
    } else {
      try {
        var p = await APIClient.getKeyTokenPreviewByID(userAcc, widget.keytokenID);
        setState(() {
          keytokenPreview = p;
        });

        if (p.ownerUserID == userAcc.userID) {
          var r = await APIClient.getKeyToken(userAcc, widget.keytokenID);
          setState(() {
            keytoken = r;
          });
        } else {
          setState(() {
            keytoken = null;
          });
        }
      } catch (exc, trace) {
        ApplicationLog.error('Failed to load data: ' + exc.toString(), trace: trace);
        Toaster.error("Error", 'Failed to load data');
        this.errorMessage = 'Failed to load data: ' + exc.toString();
        this.loadingState = KeyTokenViewPageInitState.error;
        return;
      }
    }

    setState(() {
      this.loadingState = KeyTokenViewPageInitState.okay;

      assert(keytokenPreview != null);

      if (this.keytokenPreview!.ownerUserID == userAcc.userID) {
        var cacheUser = userAcc.getUserOrNull();
        if (cacheUser != null) {
          _futureOwner = ImmediateFuture<UserPreview>.ofValue(cacheUser.toPreview());
        } else {
          _futureOwner = ImmediateFuture<UserPreview>.ofFuture(_getOwner(userAcc));
        }
      } else {
        _futureOwner = ImmediateFuture<UserPreview>.ofFuture(APIClient.getUserPreview(userAcc, this.keytokenPreview!.ownerUserID));
      }
    });
  }

  @override
  void dispose() {
    _ctrlName.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final userAcc = Provider.of<AppAuth>(context, listen: false);

    var title = "Key";

    Widget child;

    if (loadingState == KeyTokenViewPageInitState.loading) {
      child = Center(child: CircularProgressIndicator());
    } else if (loadingState == KeyTokenViewPageInitState.error) {
      child = ErrorDisplay(errorMessage: errorMessage);
    } else if (loadingState == KeyTokenViewPageInitState.okay && keytokenPreview!.ownerUserID == userAcc.userID) {
      child = _buildOwnedKeyTokenView(context, this.keytoken!);
      title = this.keytoken!.name;
    } else {
      child = _buildForeignKeyTokenView(context, this.keytokenPreview!);
      title = keytokenPreview!.name;
    }

    return SCNScaffold(
      title: title,
      showSearch: false,
      showShare: false,
      child: child,
    );
  }

  Widget _buildOwnedKeyTokenView(BuildContext context, KeyToken keytoken) {
    return SingleChildScrollView(
      child: Padding(
        padding: const EdgeInsets.fromLTRB(24, 16, 24, 16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            SizedBox(height: 8),
            UI.metaCard(
              context: context,
              icon: FontAwesomeIcons.solidIdCardClip,
              title: 'KeyTokenID',
              values: [keytoken.keytokenID],
            ),
            _buildNameCard(context, true),
            UI.metaCard(
              context: context,
              icon: FontAwesomeIcons.clock,
              title: 'Created',
              values: [_KeyTokenViewPageState._dateFormat.format(DateTime.parse(keytoken.timestampCreated).toLocal())],
            ),
            UI.metaCard(
              context: context,
              icon: FontAwesomeIcons.clockTwo,
              title: 'Last Used',
              values: [(keytoken.timestampLastUsed == null) ? 'Never' : _KeyTokenViewPageState._dateFormat.format(DateTime.parse(keytoken.timestampLastUsed!).toLocal())],
            ),
            _buildOwnerCard(context, true),
            UI.metaCard(
              context: context,
              icon: FontAwesomeIcons.solidEnvelope,
              title: 'Messages',
              values: [keytoken.messagesSent.toString()],
              mainAction: () {
                Navi.push(context, () => FilteredMessageViewPage(title: keytoken.name, filter: MessageFilter(usedKeys: [keytoken.keytokenID])));
              },
            ),
            ..._buildPermissionCard(context, true, keytoken.toPreview()),
            UI.button(text: "Delete Key", onPressed: _deleteKey, color: Colors.red[900]),
          ],
        ),
      ),
    );
  }

  Widget _buildForeignKeyTokenView(BuildContext context, KeyTokenPreview keytoken) {
    return SingleChildScrollView(
      child: Padding(
        padding: const EdgeInsets.fromLTRB(24, 16, 24, 16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            SizedBox(height: 8),
            UI.metaCard(
              context: context,
              icon: FontAwesomeIcons.solidIdCardClip,
              title: 'KeyTokenID',
              values: [keytoken.keytokenID],
            ),
            _buildNameCard(context, false),
            _buildOwnerCard(context, false),
            ..._buildPermissionCard(context, false, keytoken),
          ],
        ),
      ),
    );
  }

  Widget _buildOwnerCard(BuildContext context, bool isOwned) {
    return FutureBuilder(
      future: _futureOwner.future,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return UI.metaCard(
            context: context,
            icon: FontAwesomeIcons.solidUser,
            title: 'Owner',
            values: [keytokenPreview!.ownerUserID + (isOwned ? ' (you)' : ''), if (snapshot.data?.username != null) snapshot.data!.username!],
          );
        } else {
          return UI.metaCard(
            context: context,
            icon: FontAwesomeIcons.solidUser,
            title: 'Owner',
            values: [keytokenPreview!.ownerUserID + (isOwned ? ' (you)' : '')],
          );
        }
      },
    );
  }

  Widget _buildNameCard(BuildContext context, bool isOwned) {
    if (_editName == EditState.editing) {
      return Padding(
        padding: EdgeInsets.symmetric(vertical: 4, horizontal: 0),
        child: UI.box(
          context: context,
          padding: EdgeInsets.fromLTRB(16, 2, 4, 2),
          child: Row(
            children: [
              Container(child: Center(child: FaIcon(FontAwesomeIcons.solidInputText, size: 18)), height: 43),
              SizedBox(width: 16),
              Expanded(
                child: TextField(
                  autofocus: true,
                  controller: _ctrlName,
                  decoration: new InputDecoration.collapsed(hintText: 'Name'),
                ),
              ),
              SizedBox(width: 12),
              SizedBox(width: 4),
              IconButton(icon: FaIcon(FontAwesomeIcons.solidFloppyDisk), onPressed: _saveName),
            ],
          ),
        ),
      );
    } else if (_editName == EditState.none) {
      return UI.metaCard(
        context: context,
        icon: FontAwesomeIcons.solidInputText,
        title: 'Name',
        values: [_nameOverride ?? keytokenPreview!.name],
        iconActions: isOwned ? [(FontAwesomeIcons.penToSquare, null, _showEditName)] : [],
      );
    } else if (_editName == EditState.saving) {
      return Padding(
        padding: EdgeInsets.symmetric(vertical: 4, horizontal: 0),
        child: UI.box(
          context: context,
          padding: EdgeInsets.fromLTRB(16, 2, 4, 2),
          child: Row(
            children: [
              Container(child: Center(child: FaIcon(FontAwesomeIcons.solidInputText, size: 18)), height: 43),
              SizedBox(width: 16),
              Expanded(child: SizedBox()),
              SizedBox(width: 12),
              SizedBox(width: 4),
              Padding(padding: const EdgeInsets.all(8.0), child: SizedBox(width: 18, height: 18, child: CircularProgressIndicator())),
            ],
          ),
        ),
      );
    } else {
      throw 'Invalid EditDisplayNameState: $_editName';
    }
  }

  void _showEditName() {
    setState(() {
      _ctrlName.text = _nameOverride ?? keytokenPreview?.name ?? '';
      _editName = EditState.editing;
      if (_editName == EditState.editing) _editName = EditState.none;
    });
  }

  void _saveName() async {
    final userAcc = Provider.of<AppAuth>(context, listen: false);

    final newName = _ctrlName.text;

    try {
      setState(() {
        _editName = EditState.saving;
      });

      final newKeyToken = await APIClient.updateKeyToken(userAcc, widget.keytokenID, name: newName);

      setState(() {
        _editName = EditState.none;
        _nameOverride = newKeyToken.name;
      });

      widget.needsReload?.call();
    } catch (exc, trace) {
      ApplicationLog.error('Failed to save DisplayName: ' + exc.toString(), trace: trace);
      Toaster.error("Error", 'Failed to save DisplayName');
    }
  }

  Future<UserPreview> _getOwner(AppAuth auth) async {
    try {
      await Future.delayed(const Duration(seconds: 0), () {}); // this is annoyingly important - otherwise we call setLoadingIndeterminate directly in initStat() and get an exception....

      _incLoadingIndeterminateCounter(1);

      final owner = APIClient.getUserPreview(auth, keytokenPreview!.ownerUserID);

      //await Future.delayed(const Duration(seconds: 10), () {});

      return owner;
    } finally {
      _incLoadingIndeterminateCounter(-1);
    }
  }

  void _incLoadingIndeterminateCounter(int delta) {
    setState(() {
      _loadingIndeterminateCounter += delta;
      AppBarState().setLoadingIndeterminate(_loadingIndeterminateCounter > 0);
    });
  }

  List<Widget> _buildPermissionCard(BuildContext context, bool isOwned, KeyTokenPreview keyToken) {
    Widget w1;
    Widget w2;

    if (isOwned) {
      w1 = UI.metaCard(
        context: context,
        icon: FontAwesomeIcons.shieldKeyhole,
        title: 'Permissions',
        values: _formatPermissions(keyToken.permissions),
        iconActions: [(FontAwesomeIcons.penToSquare, null, _editPermissions)],
      );
    } else {
      w1 = UI.metaCard(
        context: context,
        icon: FontAwesomeIcons.shieldKeyhole,
        title: 'Permissions',
        values: _formatPermissions(keyToken.permissions),
      );
    }

    if (isOwned) {
      w2 = UI.metaCard(
        context: context,
        icon: FontAwesomeIcons.solidSnake,
        title: 'Channels',
        values: (keyToken.allChannels) ? ['All Channels'] : keyToken.channels, //TODO show channel names
        iconActions: [(FontAwesomeIcons.penToSquare, null, _editChannels)],
      );
    } else {
      w2 = UI.metaCard(
        context: context,
        icon: FontAwesomeIcons.solidSnake,
        title: 'Channels',
        values: (keyToken.allChannels) ? ['All Channels'] : keyToken.channels, //TODO show channel names
      );
    }

    return [w1, w2];
  }

  List<String> _formatPermissions(String v) {
    var splt = v.split(';');

    if (splt.length == 0) return ["None"];

    List<String> result = [];

    if (splt.contains("A")) result.add("Admin");
    if (splt.contains("UR")) result.add("Read Account");
    if (splt.contains("CR")) result.add("Read Messages");
    if (splt.contains("CS")) result.add("Send Messages");

    return result;
  }

  void _editPermissions() {
    final acc = Provider.of<AppAuth>(context, listen: false);

    //TODO prevent editing current admin/read token

    //TODO

    Toaster.info("Not implemented", "Currently not implemented");
  }

  void _editChannels() {
    final acc = Provider.of<AppAuth>(context, listen: false);

    //TODO prevent editing current admin/read token

    //TODO

    Toaster.info("Not implemented", "Currently not implemented");
  }

  void _deleteKey() {
    final acc = Provider.of<AppAuth>(context, listen: false);

    //TODO prevent deleting current admin/read token

    //TODO

    Toaster.info("Not implemented", "Currently not implemented");
  }
}