import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

import '../../common.dart';
import '../../models/model.dart';
import '../../models/platform_model.dart';

void clientClose(String id, OverlayDialogManager dialogManager) {
  msgBox(id, 'info', 'Close', 'Are you sure to close the connection?', '',
      dialogManager);
}

void showSuccess() {
  showToast(translate("Successful"));
}

void showError() {
  showToast(translate("Error"));
}

void showRestartRemoteDevice(
    PeerInfo pi, String id, OverlayDialogManager dialogManager) async {
  final res =
      await dialogManager.show<bool>((setState, close) => CustomAlertDialog(
            title: Row(children: [
              Icon(Icons.warning_amber_sharp,
                  color: Colors.redAccent, size: 28),
              SizedBox(width: 10),
              Text(translate("Restart Remote Device")),
            ]),
            content: Text(
                "${translate('Are you sure you want to restart')} \n${pi.username}@${pi.hostname}($id) ?"),
            onCancel: close,
            onSubmit: () => close(true),
            actions: [
              dialogButton("Cancel", onPressed: close, isOutline: true),
              dialogButton("OK", onPressed: () => close(true)),
            ],
          ));
  if (res == true) bind.sessionRestartRemoteDevice(id: id);
}

void setPermanentPasswordDialog(OverlayDialogManager dialogManager) async {
  final pw = await bind.mainGetPermanentPassword();
  final p0 = TextEditingController(text: pw);
  final p1 = TextEditingController(text: pw);
  var validateLength = false;
  var validateSame = false;
  dialogManager.show((setState, close) {
    submit() async {
      close();
      dialogManager.showLoading(translate("Waiting"));
      if (await gFFI.serverModel.setPermanentPassword(p0.text)) {
        dialogManager.dismissAll();
        showSuccess();
      } else {
        dialogManager.dismissAll();
        showError();
      }
    }

    return CustomAlertDialog(
      title: Text(translate('Set your own password')),
      content: Form(
          autovalidateMode: AutovalidateMode.onUserInteraction,
          child: Column(mainAxisSize: MainAxisSize.min, children: [
            TextFormField(
              autofocus: true,
              obscureText: true,
              keyboardType: TextInputType.visiblePassword,
              decoration: InputDecoration(
                labelText: translate('Password'),
              ),
              controller: p0,
              validator: (v) {
                if (v == null) return null;
                final val = v.trim().length > 5;
                if (validateLength != val) {
                  // use delay to make setState success
                  Future.delayed(Duration(microseconds: 1),
                      () => setState(() => validateLength = val));
                }
                return val
                    ? null
                    : translate('Too short, at least 6 characters.');
              },
            ),
            TextFormField(
              obscureText: true,
              keyboardType: TextInputType.visiblePassword,
              decoration: InputDecoration(
                labelText: translate('Confirmation'),
              ),
              controller: p1,
              validator: (v) {
                if (v == null) return null;
                final val = p0.text == v;
                if (validateSame != val) {
                  Future.delayed(Duration(microseconds: 1),
                      () => setState(() => validateSame = val));
                }
                return val
                    ? null
                    : translate('The confirmation is not identical.');
              },
            ),
          ])),
      onCancel: close,
      onSubmit: (validateLength && validateSame) ? submit : null,
      actions: [
        dialogButton(
          'Cancel',
          onPressed: close,
          isOutline: true,
        ),
        dialogButton(
          'OK',
          onPressed: (validateLength && validateSame) ? submit : null,
        ),
      ],
    );
  });
}

void setTemporaryPasswordLengthDialog(
    OverlayDialogManager dialogManager) async {
  List<String> lengths = ['6', '8', '10'];
  String length = await bind.mainGetOption(key: "temporary-password-length");
  var index = lengths.indexOf(length);
  if (index < 0) index = 0;
  length = lengths[index];
  dialogManager.show((setState, close) {
    setLength(newValue) {
      final oldValue = length;
      if (oldValue == newValue) return;
      setState(() {
        length = newValue;
      });
      bind.mainSetOption(key: "temporary-password-length", value: newValue);
      bind.mainUpdateTemporaryPassword();
      Future.delayed(Duration(milliseconds: 200), () {
        close();
        showSuccess();
      });
    }

    return CustomAlertDialog(
      title: Text(translate("Set temporary password length")),
      content: Column(
          mainAxisSize: MainAxisSize.min,
          children:
              lengths.map((e) => getRadio(e, e, length, setLength)).toList()),
      actions: [],
      contentPadding: 14,
    );
  }, backDismiss: true, clickMaskDismiss: true);
}

void enterPasswordDialog(String id, OverlayDialogManager dialogManager) async {
  final controller = TextEditingController();
  var remember = await bind.sessionGetRemember(id: id) ?? false;
  dialogManager.dismissAll();
  dialogManager.show((setState, close) {
    cancel() {
      close();
      closeConnection();
    }

    submit() {
      var text = controller.text.trim();
      if (text == '') return;
      gFFI.login(id, text, remember);
      close();
      dialogManager.showLoading(translate('Logging in...'),
          onCancel: closeConnection);
    }

    return CustomAlertDialog(
      title: Text(translate('Password Required')),
      content: Column(mainAxisSize: MainAxisSize.min, children: [
        PasswordWidget(controller: controller),
        CheckboxListTile(
          contentPadding: const EdgeInsets.all(0),
          dense: true,
          controlAffinity: ListTileControlAffinity.leading,
          title: Text(
            translate('Remember password'),
          ),
          value: remember,
          onChanged: (v) {
            if (v != null) {
              setState(() => remember = v);
            }
          },
        ),
      ]),
      actions: [
        dialogButton('Cancel', onPressed: cancel, isOutline: true),
        dialogButton('OK', onPressed: submit),
      ],
      onSubmit: submit,
      onCancel: cancel,
    );
  });
}

void wrongPasswordDialog(
    String id, OverlayDialogManager dialogManager, type, title, text) {
  dialogManager.dismissAll();
  dialogManager.show((setState, close) {
    cancel() {
      close();
      closeConnection();
    }

    submit() {
      enterPasswordDialog(id, dialogManager);
    }

    return CustomAlertDialog(
        title: null,
        content: msgboxContent(type, title, text),
        onSubmit: submit,
        onCancel: cancel,
        actions: [
          dialogButton(
            'Cancel',
            onPressed: cancel,
            isOutline: true,
          ),
          dialogButton(
            'Retry',
            onPressed: submit,
          ),
        ]);
  });
}

void showServerSettingsWithValue(
    ServerConfig serverConfig, OverlayDialogManager dialogManager) async {
  Map<String, dynamic> oldOptions = jsonDecode(await bind.mainGetOptions());
  final oldCfg = ServerConfig.fromOptions(oldOptions);

  var isInProgress = false;
  final idCtrl = TextEditingController(text: serverConfig.idServer);
  final relayCtrl = TextEditingController(text: serverConfig.relayServer);
  final apiCtrl = TextEditingController(text: serverConfig.apiServer);
  final keyCtrl = TextEditingController(text: serverConfig.key);

  String? idServerMsg;
  String? relayServerMsg;
  String? apiServerMsg;

  dialogManager.show((setState, close) {
    Future<bool> validate() async {
      if (idCtrl.text != oldCfg.idServer) {
        final res = await validateAsync(idCtrl.text);
        setState(() => idServerMsg = res);
        if (idServerMsg != null) return false;
      }
      if (relayCtrl.text != oldCfg.relayServer) {
        relayServerMsg = await validateAsync(relayCtrl.text);
        if (relayServerMsg != null) return false;
      }
      if (apiCtrl.text != oldCfg.apiServer) {
        if (apiServerMsg != null) return false;
      }
      return true;
    }

    return CustomAlertDialog(
      title: Text(translate('ID/Relay Server')),
      content: Form(
          child: Column(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                    TextFormField(
                      controller: idCtrl,
                      decoration: InputDecoration(
                          labelText: translate('ID Server'),
                          errorText: idServerMsg),
                    )
                  ] +
                  (isAndroid
                      ? [
                          TextFormField(
                            controller: relayCtrl,
                            decoration: InputDecoration(
                                labelText: translate('Relay Server'),
                                errorText: relayServerMsg),
                          )
                        ]
                      : []) +
                  [
                    TextFormField(
                      controller: apiCtrl,
                      decoration: InputDecoration(
                        labelText: translate('API Server'),
                      ),
                      autovalidateMode: AutovalidateMode.onUserInteraction,
                      validator: (v) {
                        if (v != null && v.isNotEmpty) {
                          if (!(v.startsWith('http://') ||
                              v.startsWith("https://"))) {
                            return translate("invalid_http");
                          }
                        }
                        return apiServerMsg;
                      },
                    ),
                    TextFormField(
                      controller: keyCtrl,
                      decoration: InputDecoration(
                        labelText: 'Key',
                      ),
                    ),
                    Offstage(
                        offstage: !isInProgress,
                        child: LinearProgressIndicator())
                  ])),
      actions: [
        dialogButton('Cancel', onPressed: () {
          close();
        }, isOutline: true),
        dialogButton(
          'OK',
          onPressed: () async {
            setState(() {
              idServerMsg = null;
              relayServerMsg = null;
              apiServerMsg = null;
              isInProgress = true;
            });
            if (await validate()) {
              if (idCtrl.text != oldCfg.idServer) {
                if (oldCfg.idServer.isNotEmpty) {
                  await gFFI.userModel.logOut();
                }
                bind.mainSetOption(
                    key: "custom-rendezvous-server", value: idCtrl.text);
              }
              if (relayCtrl.text != oldCfg.relayServer) {
                bind.mainSetOption(key: "relay-server", value: relayCtrl.text);
              }
              if (keyCtrl.text != oldCfg.key) {
                bind.mainSetOption(key: "key", value: keyCtrl.text);
              }
              if (apiCtrl.text != oldCfg.apiServer) {
                bind.mainSetOption(key: "api-server", value: apiCtrl.text);
              }
              close();
              showToast(translate('Successful'));
            }
            setState(() {
              isInProgress = false;
            });
          },
        ),
      ],
    );
  });
}

void showWaitUacDialog(
    String id, OverlayDialogManager dialogManager, String type) {
  dialogManager.dismissAll();
  dialogManager.show(
      tag: '$id-wait-uac',
      (setState, close) => CustomAlertDialog(
            title: null,
            content: msgboxContent(type, 'Wait', 'wait_accept_uac_tip'),
          ));
}

void _showRequestElevationDialog(
    String id, OverlayDialogManager dialogManager) {
  RxString groupValue = ''.obs;
  RxString errUser = ''.obs;
  RxString errPwd = ''.obs;
  TextEditingController userController = TextEditingController();
  TextEditingController pwdController = TextEditingController();

  void onRadioChanged(String? value) {
    if (value != null) {
      groupValue.value = value;
    }
  }

  const minTextStyle = TextStyle(fontSize: 14);

  var content = Obx(() => Column(children: [
        Row(
          children: [
            Radio(
                value: '',
                groupValue: groupValue.value,
                onChanged: onRadioChanged),
            Expanded(
                child:
                    Text(translate('Ask the remote user for authentication'))),
          ],
        ),
        Align(
          alignment: Alignment.centerLeft,
          child: Text(
                  translate(
                      'Choose this if the remote account is administrator'),
                  style: TextStyle(fontSize: 13))
              .marginOnly(left: 40),
        ).marginOnly(bottom: 15),
        Row(
          children: [
            Radio(
                value: 'logon',
                groupValue: groupValue.value,
                onChanged: onRadioChanged),
            Expanded(
              child: Text(translate(
                  'Transmit the username and password of administrator')),
            )
          ],
        ),
        Row(
          children: [
            Expanded(
                flex: 1,
                child: Text(
                  '${translate('Username')}:',
                  style: minTextStyle,
                ).marginOnly(right: 10)),
            Expanded(
              flex: 3,
              child: TextField(
                controller: userController,
                style: minTextStyle,
                decoration: InputDecoration(
                    isDense: true,
                    contentPadding: EdgeInsets.symmetric(vertical: 15),
                    hintText: 'eg: admin',
                    errorText: errUser.isEmpty ? null : errUser.value),
                onChanged: (s) {
                  if (s.isNotEmpty) {
                    errUser.value = '';
                  }
                },
              ),
            )
          ],
        ).marginOnly(left: 40),
        Row(
          children: [
            Expanded(
                flex: 1,
                child: Text(
                  '${translate('Password')}:',
                  style: minTextStyle,
                ).marginOnly(right: 10)),
            Expanded(
              flex: 3,
              child: TextField(
                controller: pwdController,
                obscureText: true,
                style: minTextStyle,
                decoration: InputDecoration(
                    isDense: true,
                    contentPadding: EdgeInsets.symmetric(vertical: 15),
                    errorText: errPwd.isEmpty ? null : errPwd.value),
                onChanged: (s) {
                  if (s.isNotEmpty) {
                    errPwd.value = '';
                  }
                },
              ),
            ),
          ],
        ).marginOnly(left: 40),
        Align(
            alignment: Alignment.centerLeft,
            child: Text(translate('still_click_uac_tip'),
                    style: TextStyle(fontSize: 13, fontWeight: FontWeight.bold))
                .marginOnly(top: 20)),
      ]));

  dialogManager.dismissAll();
  dialogManager.show(tag: '$id-request-elevation', (setState, close) {
    void submit() {
      if (groupValue.value == 'logon') {
        if (userController.text.isEmpty) {
          errUser.value = translate('Empty Username');
          return;
        }
        if (pwdController.text.isEmpty) {
          errPwd.value = translate('Empty Password');
          return;
        }
        bind.sessionElevateWithLogon(
            id: id,
            username: userController.text,
            password: pwdController.text);
      } else {
        bind.sessionElevateDirect(id: id);
      }
    }

    return CustomAlertDialog(
      title: Text(translate('Request Elevation')),
      content: content,
      actions: [
        dialogButton('Cancel', onPressed: close, isOutline: true),
        dialogButton('OK', onPressed: submit),
      ],
      onSubmit: submit,
      onCancel: close,
    );
  });
}

void showOnBlockDialog(
  String id,
  String type,
  String title,
  String text,
  OverlayDialogManager dialogManager,
) {
  if (dialogManager.existing('$id-wait-uac') ||
      dialogManager.existing('$id-request-elevation')) {
    return;
  }
  dialogManager.show(tag: '$id-$type', (setState, close) {
    void submit() {
      close();
      _showRequestElevationDialog(id, dialogManager);
    }

    return CustomAlertDialog(
      title: null,
      content: msgboxContent(type, title,
          "${translate(text)}${type.contains('uac') ? '\n' : '\n\n'}${translate('request_elevation_tip')}"),
      actions: [
        dialogButton('Wait', onPressed: close, isOutline: true),
        dialogButton('Request Elevation', onPressed: submit),
      ],
      onSubmit: submit,
      onCancel: close,
    );
  });
}

void showElevationError(String id, String type, String title, String text,
    OverlayDialogManager dialogManager) {
  dialogManager.show(tag: '$id-$type', (setState, close) {
    void submit() {
      close();
      _showRequestElevationDialog(id, dialogManager);
    }

    return CustomAlertDialog(
      title: null,
      content: msgboxContent(type, title, text),
      actions: [
        dialogButton('Cancel', onPressed: () {
          close();
        }, isOutline: true),
        dialogButton('Retry', onPressed: submit),
      ],
      onSubmit: submit,
      onCancel: close,
    );
  });
}

void showWaitAcceptDialog(String id, String type, String title, String text,
    OverlayDialogManager dialogManager) {
  dialogManager.dismissAll();
  dialogManager.show((setState, close) {
    onCancel() {
      closeConnection();
    }

    return CustomAlertDialog(
      title: null,
      content: msgboxContent(type, title, text),
      actions: [
        dialogButton('Cancel', onPressed: onCancel, isOutline: true),
      ],
      onCancel: onCancel,
    );
  });
}

Future<String?> validateAsync(String value) async {
  value = value.trim();
  if (value.isEmpty) {
    return null;
  }
  final res = await bind.mainTestIfValidServer(server: value);
  return res.isEmpty ? null : res;
}

class PasswordWidget extends StatefulWidget {
  PasswordWidget({Key? key, required this.controller, this.autoFocus = true})
      : super(key: key);

  final TextEditingController controller;
  final bool autoFocus;

  @override
  State<PasswordWidget> createState() => _PasswordWidgetState();
}

class _PasswordWidgetState extends State<PasswordWidget> {
  bool _passwordVisible = false;
  final _focusNode = FocusNode();

  @override
  void initState() {
    super.initState();
    if (widget.autoFocus) {
      Timer(Duration(milliseconds: 50), () => _focusNode.requestFocus());
    }
  }

  @override
  void dispose() {
    _focusNode.unfocus();
    _focusNode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return TextField(
      focusNode: _focusNode,
      controller: widget.controller,
      obscureText: !_passwordVisible,
      //This will obscure text dynamically
      keyboardType: TextInputType.visiblePassword,
      decoration: InputDecoration(
        labelText: translate('Password'),
        hintText: translate('Enter your password'),
        // Here is key idea
        suffixIcon: IconButton(
          icon: Icon(
              // Based on passwordVisible state choose the icon
              _passwordVisible ? Icons.visibility : Icons.visibility_off,
              color: MyTheme.lightTheme.primaryColor),
          onPressed: () {
            // Update the state i.e. toggle the state of passwordVisible variable
            setState(() {
              _passwordVisible = !_passwordVisible;
            });
          },
        ),
      ),
    );
  }
}