import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:logging/logging.dart';
import 'package:string_validator/string_validator.dart';

import '../controls/padding.dart';
import '../globals.dart';
import '../models/auth/basic_credentials.dart';
import '../models/auth/credentials_intf.dart';
import '../models/auth/oauth_credentials.dart';
import '../models/auth/profile.dart';
import '../routes.dart';
import '../services/auth_service.dart';
import '../utils/snackbar_builder.dart';

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

  @override
  State<SignInScreen> createState() => _SignInScreenState();
}

class _SignInScreenState extends State<SignInScreen> {
  static final _logger = Logger('$SignInScreen');
  static const usernamePasswordType = 'Username/Password';
  static const oauthType = 'Standard Login';
  static final authTypes = [usernamePasswordType, oauthType];
  final formKey = GlobalKey<FormState>();
  final usernameController = TextEditingController();
  final serverNameController = TextEditingController();
  final passwordController = TextEditingController();
  var authType = oauthType;
  var hidePassword = true;
  var signInButtonEnabled = false;
  Profile? existingProfile;

  bool get showUsernameAndPasswordFields => authType == usernamePasswordType;

  bool get existingAccount => existingProfile != null;

  @override
  void initState() {
    super.initState();
    final service = getIt<AccountsService>();
    if (service.loggedIn) {
      setCredentials(null, service.currentProfile);
    } else {
      newProfile();
    }
    usernameController.addListener(() {
      _updateSignInButtonStatus();
    });
    passwordController.addListener(() {
      _updateSignInButtonStatus();
    });
    serverNameController.addListener(() {
      _updateSignInButtonStatus();
    });
  }

  void _updateSignInButtonStatus() {
    setState(() {
      signInButtonEnabled = switch (oauthType) {
        usernamePasswordType =>
        serverNameController.text.isNotEmpty &&
            usernameController.text.isNotEmpty &&
            passwordController.text.isNotEmpty,
        oauthType => serverNameController.text.isNotEmpty,
        _ => false,
      };
    });
  }

  void newProfile() {
    usernameController.clear();
    passwordController.clear();
    serverNameController.clear();
    authType = oauthType;
    signInButtonEnabled = false;
    existingProfile = null;
  }

  void setBasicCredentials(BasicCredentials credentials) {
    usernameController.text = credentials.username;
    passwordController.text = credentials.password;
    serverNameController.text = credentials.serverName;
    authType = usernamePasswordType;
  }

  void setOauthCredentials(OAuthCredentials credentials) {
    serverNameController.text = credentials.serverName;
    authType = oauthType;
  }

  void setCredentials(BuildContext? context, Profile profile) {
    final ICredentials credentials = profile.credentials;
    existingProfile = profile;
    signInButtonEnabled = !profile.loggedIn;
    if (credentials is BasicCredentials) {
      setBasicCredentials(credentials);
      return;
    }

    if (credentials is OAuthCredentials) {
      setOauthCredentials(credentials);
      return;
    }

    final msg = 'Unknown credentials type: ${credentials.runtimeType}';
    _logger.severe(msg);

    if (context?.mounted ?? false) {
      buildSnackbar(context!, msg);
    }
  }

  @override
  Widget build(BuildContext context) {
    final service = getIt<AccountsService>();
    final loggedInProfiles = service.loggedInProfiles;
    final loggedOutProfiles = service.loggedOutProfiles;
    return Scaffold(
      appBar: AppBar(
        title: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            const Text('Sign In'),
            IconButton(
              icon: const Icon(Icons.add),
              tooltip: 'Add new account',
              onPressed: () {
                newProfile();
                setState(() {});
              },
            )
          ],
        ),
      ),
      body: Padding(
        padding: const EdgeInsets.all(20.0),
        child: Form(
          key: formKey,
          child: Center(
            child: ListView(
              children: [
                Center(
                  child: DropdownButton<String>(
                      value: authType,
                      items: authTypes
                          .map(
                              (a) => DropdownMenuItem(value: a, child: Text(a)))
                          .toList(),
                      onChanged: existingAccount
                          ? null
                          : (value) {
                        if (existingAccount) {
                          buildSnackbar(context,
                              "Can't change the type on an existing account");
                          return;
                        }
                        authType = value!;
                        setState(() {});
                      }),
                ),
                const VerticalPadding(),
                TextFormField(
                  autocorrect: false,
                  readOnly: existingAccount,
                  autovalidateMode: AutovalidateMode.onUserInteraction,
                  controller: serverNameController,
                  validator: (value) =>
                  isFQDN(value ?? '') ? null : 'Not a valid server name',
                  decoration: InputDecoration(
                    hintText: 'Server Name (friendica.example.com)',
                    border: OutlineInputBorder(
                      borderSide: BorderSide(
                        color: Theme
                            .of(context)
                            .colorScheme
                            .background,
                      ),
                      borderRadius: BorderRadius.circular(5.0),
                    ),
                    labelText: 'Server Name',
                  ),
                ),
                const VerticalPadding(),
                if (!showUsernameAndPasswordFields) ...[
                  Text(
                    existingAccount
                        ? 'Configured to sign in as user ${existingProfile
                        ?.handle}'
                        : 'Relatica will open the requested Friendica site in a web browser where you will be asked to authorize this client.',
                    softWrap: true,
                  ),
                  const VerticalPadding(),
                ],
                if (showUsernameAndPasswordFields) ...[
                  TextFormField(
                    readOnly: existingAccount,
                    autovalidateMode: AutovalidateMode.onUserInteraction,
                    controller: usernameController,
                    keyboardType: TextInputType.emailAddress,
                    validator: (value) {
                      if (value == null) {
                        return null;
                      }

                      if (value.contains('@')) {
                        return isEmail(value)
                            ? null
                            : 'Not a valid Friendica Account Address';
                      }

                      return isAlphanumeric(value.replaceAll('-', ''))
                          ? null
                          : 'Username should be alpha-numeric';
                    },
                    decoration: InputDecoration(
                      prefixIcon: const Icon(Icons.alternate_email),
                      hintText: 'Username (user@example.com)',
                      border: OutlineInputBorder(
                        borderSide: BorderSide(
                          color: Theme
                              .of(context)
                              .colorScheme
                              .background,
                        ),
                        borderRadius: BorderRadius.circular(5.0),
                      ),
                      labelText: 'Username',
                    ),
                  ),
                  const VerticalPadding(),
                  TextFormField(
                    readOnly: existingAccount,
                    obscureText: hidePassword,
                    controller: passwordController,
                    validator: (value) {
                      if (value == null || value.isEmpty) {
                        return 'Password field cannot be empty';
                      }

                      return null;
                    },
                    decoration: InputDecoration(
                      prefixIcon: const Icon(Icons.password),
                      suffixIcon: IconButton(
                        onPressed: () {
                          setState(() {
                            hidePassword = !hidePassword;
                          });
                        },
                        icon: hidePassword
                            ? const Icon(Icons.remove_red_eye_outlined)
                            : const Icon(Icons.remove_red_eye),
                      ),
                      hintText: 'Password',
                      border: OutlineInputBorder(
                        borderSide: BorderSide(
                          color: Theme
                              .of(context)
                              .colorScheme
                              .background,
                        ),
                        borderRadius: BorderRadius.circular(5.0),
                      ),
                      labelText: 'Password',
                    ),
                  ),
                  const VerticalPadding(),
                ],
                signInButtonEnabled
                    ? ElevatedButton(
                  onPressed: () async => await _signIn(context),
                  child: const Text('Signin'),
                )
                    : SizedBox(),
                const VerticalPadding(),
                Text(
                  'Logged out:',
                  style: Theme
                      .of(context)
                      .textTheme
                      .headlineSmall,
                ),
                loggedOutProfiles.isEmpty
                    ? const Text(
                  'No logged out profiles',
                  textAlign: TextAlign.center,
                )
                    : Container(
                  decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(10),
                      border: Border.all(
                        width: 0.5,
                      )),
                  child: Column(
                    children: loggedOutProfiles.map((p) {
                      return ListTile(
                        onTap: () {
                          setCredentials(context, p);
                          setState(() {});
                        },
                        title: Text(p.handle),
                        subtitle: Text(p.credentials is BasicCredentials
                            ? usernamePasswordType
                            : oauthType),
                        trailing: ElevatedButton(
                          onPressed: () async {
                            final confirm = await showYesNoDialog(context,
                                'Remove login information from app?');
                            if (confirm ?? false) {
                              await service.removeProfile(p);
                            }
                            setState(() {});
                          },
                          child: const Text('Remove'),
                        ),
                      );
                    }).toList(),
                  ),
                ),
                const VerticalPadding(),
                Text(
                  'Logged in:',
                  style: Theme
                      .of(context)
                      .textTheme
                      .headlineSmall,
                ),
                loggedInProfiles.isEmpty
                    ? const Text(
                  'No logged in profiles',
                  textAlign: TextAlign.center,
                )
                    : Container(
                  decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(10),
                      border: Border.all(
                        width: 0.5,
                      )),
                  child: Column(
                    children: loggedInProfiles.map((p) {
                      final active = service.loggedIn
                          ? p.id == service.currentProfile.id
                          : false;
                      return ListTile(
                        onTap: () async {
                          setCredentials(context, p);
                          setState(() {});
                        },
                        title: Text(
                          p.handle,
                          style: active
                              ? const TextStyle(
                              fontWeight: FontWeight.bold,
                              fontStyle: FontStyle.italic)
                              : null,
                        ),
                        subtitle: Text(
                          p.credentials is BasicCredentials
                              ? usernamePasswordType
                              : oauthType,
                          style: active
                              ? const TextStyle(
                              fontWeight: FontWeight.bold,
                              fontStyle: FontStyle.italic)
                              : null,
                        ),
                        trailing: ElevatedButton(
                          onPressed: () async {
                            final confirm = await showYesNoDialog(
                                context, 'Log out account?');
                            if (confirm == true) {
                              await getIt<AccountsService>().signOut(p);
                              setState(() {});
                            }
                          },
                          child: const Text('Sign out'),
                        ),
                      );
                    }).toList(),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Future<void> _signIn(BuildContext context) async {
    final valid = formKey.currentState?.validate() ?? false;
    if (!valid) {
      buildSnackbar(
          context, 'Cannot login. Fix highlighted errors and try again.');
      return;
    }

    ICredentials? creds;
    if (existingProfile != null) {
      creds = existingProfile?.credentials;
    } else {
      switch (authType) {
        case usernamePasswordType:
          creds = BasicCredentials(
              username: usernameController.text,
              password: passwordController.text,
              serverName: serverNameController.text);
          break;
        case oauthType:
          creds = OAuthCredentials.bootstrap(serverNameController.text);
          break;
        default:
          buildSnackbar(context, 'Unknown authorization type: $authType');
          break;
      }
    }

    if (creds == null) {
      return;
    }

    buildSnackbar(context, 'Attempting to sign in account...');
    final result = await getIt<AccountsService>().signIn(
      creds,
      withNotification: false,
    );
    if (mounted && result.isFailure) {
      buildSnackbar(context, 'Error signing in: ${result.error}');
      return;
    }

    if (mounted) {
      buildSnackbar(context, 'Account signed in...');
    }
    await getIt<AccountsService>().setActiveProfile(result.value);
    if (mounted) {
      context.goNamed(ScreenPaths.timelines);
    }
  }
}
