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/channel.dart';
import 'package:simplecloudnotifier/models/subscription.dart';
import 'package:simplecloudnotifier/models/user.dart';
import 'package:simplecloudnotifier/pages/channel_view/channel_view.dart';
import 'package:simplecloudnotifier/state/app_auth.dart';
import 'package:simplecloudnotifier/state/app_bar_state.dart';
import 'package:simplecloudnotifier/state/app_settings.dart';
import 'package:simplecloudnotifier/state/application_log.dart';
import 'package:simplecloudnotifier/types/immediate_future.dart';
import 'package:simplecloudnotifier/utils/dialogs.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 SubscriptionViewPage extends StatefulWidget {
  const SubscriptionViewPage({
    required this.subscriptionID,
    required this.preloadedData,
    required this.needsReload,
    super.key,
  });

  final String subscriptionID;
  final (Subscription?, UserPreview?, UserPreview?, ChannelPreview?)? preloadedData;

  final void Function()? needsReload;

  @override
  State<SubscriptionViewPage> createState() => _SubscriptionViewPageState();
}

enum EditState { none, editing, saving }

enum SubscriptionViewPageInitState { loading, okay, error }

class _SubscriptionViewPageState extends State<SubscriptionViewPage> {
  ImmediateFuture<UserPreview> _futureChannelOwner = ImmediateFuture.ofPending();
  ImmediateFuture<UserPreview> _futureSubscriber = ImmediateFuture.ofPending();
  ImmediateFuture<ChannelPreview> _futureChannel = ImmediateFuture.ofPending();

  int _loadingIndeterminateCounter = 0;

  Subscription? subscription;

  SubscriptionViewPageInitState loadingState = SubscriptionViewPageInitState.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?.$1 != null && widget.preloadedData!.$1!.subscriptionID == widget.subscriptionID && usePreload) {
      subscription = widget.preloadedData!.$1!;
    } else {
      try {
        var r = await APIClient.getSubscription(userAcc, widget.subscriptionID);
        setState(() {
          subscription = r;
        });
      } 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 = SubscriptionViewPageInitState.error;
        return;
      }
    }

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

      assert(subscription != null);

      if (widget.preloadedData?.$2 != null && widget.preloadedData!.$2!.userID == this.subscription!.channelOwnerUserID && usePreload) {
        _futureChannelOwner = ImmediateFuture<UserPreview>.ofValue(widget.preloadedData!.$2!);
      } else if (widget.preloadedData?.$3 != null && widget.preloadedData!.$3!.userID == this.subscription!.channelOwnerUserID && usePreload) {
        _futureChannelOwner = ImmediateFuture<UserPreview>.ofValue(widget.preloadedData!.$3!);
      } else if (this.subscription!.channelOwnerUserID == userAcc.userID) {
        var cacheUser = userAcc.getUserOrNull();
        if (cacheUser != null) {
          _futureChannelOwner = ImmediateFuture<UserPreview>.ofValue(cacheUser.toPreview());
        } else {
          _futureChannelOwner = ImmediateFuture<UserPreview>.ofFuture(_getUserPreview(userAcc, this.subscription!.channelOwnerUserID));
        }
      } else {
        _futureChannelOwner = ImmediateFuture<UserPreview>.ofFuture(APIClient.getUserPreview(userAcc, this.subscription!.channelOwnerUserID));
      }

      if (widget.preloadedData?.$2 != null && widget.preloadedData!.$2!.userID == this.subscription!.subscriberUserID && usePreload) {
        _futureSubscriber = ImmediateFuture<UserPreview>.ofValue(widget.preloadedData!.$2!);
      } else if (widget.preloadedData?.$3 != null && widget.preloadedData!.$3!.userID == this.subscription!.subscriberUserID && usePreload) {
        _futureSubscriber = ImmediateFuture<UserPreview>.ofValue(widget.preloadedData!.$3!);
      } else if (this.subscription!.subscriberUserID == userAcc.userID) {
        var cacheUser = userAcc.getUserOrNull();
        if (cacheUser != null) {
          _futureSubscriber = ImmediateFuture<UserPreview>.ofValue(cacheUser.toPreview());
        } else {
          _futureSubscriber = ImmediateFuture<UserPreview>.ofFuture(_getUserPreview(userAcc, this.subscription!.subscriberUserID));
        }
      } else {
        _futureSubscriber = ImmediateFuture<UserPreview>.ofFuture(APIClient.getUserPreview(userAcc, this.subscription!.subscriberUserID));
      }

      if (widget.preloadedData?.$4 != null && widget.preloadedData!.$4!.channelID == this.subscription!.channelID && usePreload) {
        _futureChannel = ImmediateFuture<ChannelPreview>.ofValue(widget.preloadedData!.$4!);
      } else {
        _futureChannel = ImmediateFuture<ChannelPreview>.ofFuture(APIClient.getChannelPreview(userAcc, this.subscription!.channelID));
      }
    });
  }

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

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

    Widget child;

    if (loadingState == SubscriptionViewPageInitState.loading) {
      child = Center(child: CircularProgressIndicator());
    } else if (loadingState == SubscriptionViewPageInitState.error) {
      child = ErrorDisplay(errorMessage: errorMessage);
    } else if (loadingState == SubscriptionViewPageInitState.okay) {
      if (subscription!.channelOwnerUserID == userAcc.userID && subscription!.subscriberUserID == userAcc.userID) {
        child = _buildOwnedSubscriptionView(context, this.subscription!);
      } else if (subscription!.channelOwnerUserID == userAcc.userID) {
        child = _buildIncomingSubscriptionView(context, this.subscription!);
      } else if (subscription!.subscriberUserID == userAcc.userID) {
        child = _buildOutgoingSubscriptionView(context, this.subscription!);
      } else {
        child = ErrorDisplay(errorMessage: 'Invalid subscription state!');
      }
    } else {
      child = ErrorDisplay(errorMessage: 'Invalid page state!');
    }

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

  Widget _buildOwnedSubscriptionView(BuildContext context, Subscription subscription) {
    final dateFormat = context.select<AppSettings, AppSettingsDateFormat>((v) => v.dateFormat).dateFormat();

    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: 'SubscriptionID',
              values: [subscription.subscriptionID],
            ),
            _buildChannelOwnerCard(context, subscription),
            _buildSubscriberCard(context, subscription),
            _buildChannelCard(context, subscription),
            UI.metaCard(
              context: context,
              icon: FontAwesomeIcons.clock,
              title: 'Created',
              values: [dateFormat.format(DateTime.parse(subscription.timestampCreated).toLocal())],
            ),
            _buildStatusCard(context),
            UI.button(text: "Unsubscribe", onPressed: _unsubscribe, tonal: true),
          ],
        ),
      ),
    );
  }

  Widget _buildIncomingSubscriptionView(BuildContext context, Subscription subscription) {
    final dateFormat = context.select<AppSettings, AppSettingsDateFormat>((v) => v.dateFormat).dateFormat();

    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: 'SubscriptionID',
              values: [subscription.subscriptionID],
            ),
            _buildChannelOwnerCard(context, subscription),
            _buildSubscriberCard(context, subscription),
            _buildChannelCard(context, subscription),
            UI.metaCard(
              context: context,
              icon: FontAwesomeIcons.clock,
              title: 'Created',
              values: [dateFormat.format(DateTime.parse(subscription.timestampCreated).toLocal())],
            ),
            _buildStatusCard(context),
            if (subscription.confirmed) UI.button(text: "Revoke subscription", onPressed: _unsubscribe, color: Colors.red),
            if (!subscription.confirmed) UI.button(text: "Confirm subscription", onPressed: _confirm, color: Colors.green),
            if (!subscription.confirmed) UI.button(text: "Deny subscription", onPressed: _unsubscribe, color: Colors.red),
          ],
        ),
      ),
    );
  }

  Widget _buildOutgoingSubscriptionView(BuildContext context, Subscription subscription) {
    final dateFormat = context.select<AppSettings, AppSettingsDateFormat>((v) => v.dateFormat).dateFormat();

    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: 'SubscriptionID',
              values: [subscription.subscriptionID],
            ),
            _buildChannelOwnerCard(context, subscription),
            _buildSubscriberCard(context, subscription),
            _buildChannelCard(context, subscription),
            UI.metaCard(
              context: context,
              icon: FontAwesomeIcons.clock,
              title: 'Created',
              values: [dateFormat.format(DateTime.parse(subscription.timestampCreated).toLocal())],
            ),
            _buildStatusCard(context),
            if (subscription.confirmed && subscription.active) UI.button(text: "Deactivate subscription", onPressed: _deactivate, tonal: true),
            if (subscription.confirmed && !subscription.active) UI.button(text: "Activate subscription", onPressed: _activate, tonal: true),
            if (subscription.confirmed && !subscription.active) UI.button(text: "Delete subscription", onPressed: () => _unsubscribe(confirm: 'Really (permanently) delete the subscription to this channel?'), color: Colors.red),
            if (!subscription.confirmed) UI.button(text: "Cancel subscription request", onPressed: _unsubscribe, tonal: true),
          ],
        ),
      ),
    );
  }

  Widget _buildChannelOwnerCard(BuildContext context, Subscription subscription) {
    final userAcc = Provider.of<AppAuth>(context, listen: false);

    bool isSelf = subscription.channelOwnerUserID == userAcc.userID;

    return FutureBuilder(
      future: _futureChannelOwner.future,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return UI.metaCard(
            context: context,
            icon: FontAwesomeIcons.solidUser,
            title: 'Channel Owner',
            values: [subscription.channelOwnerUserID + (isSelf ? ' (you)' : ''), if (snapshot.data?.username != null) snapshot.data!.username!],
          );
        } else {
          return UI.metaCard(
            context: context,
            icon: FontAwesomeIcons.solidUser,
            title: 'Channel Owner',
            values: [subscription.channelOwnerUserID + (isSelf ? ' (you)' : '')],
          );
        }
      },
    );
  }

  Widget _buildSubscriberCard(BuildContext context, Subscription subscription) {
    final userAcc = Provider.of<AppAuth>(context, listen: false);

    bool isSelf = subscription.subscriberUserID == userAcc.userID;

    return FutureBuilder(
      future: _futureSubscriber.future,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return UI.metaCard(
            context: context,
            icon: FontAwesomeIcons.solidUser,
            title: 'Subscriber',
            values: [subscription.subscriberUserID + (isSelf ? ' (you)' : ''), if (snapshot.data?.username != null) snapshot.data!.username!],
          );
        } else {
          return UI.metaCard(
            context: context,
            icon: FontAwesomeIcons.solidUser,
            title: 'Subscriber',
            values: [subscription.subscriberUserID + (isSelf ? ' (you)' : '')],
          );
        }
      },
    );
  }

  Widget _buildChannelCard(BuildContext context, Subscription subscription) {
    return FutureBuilder(
      future: _futureChannel.future,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return UI.metaCard(
            context: context,
            icon: FontAwesomeIcons.solidSnake,
            title: 'Channel',
            values: [subscription.channelID, snapshot.data!.displayName],
            mainAction: () => Navi.push(context, () => ChannelViewPage(channelID: subscription.channelID, preloadedData: null, needsReload: null)),
          );
        } else {
          return UI.metaCard(
            context: context,
            icon: FontAwesomeIcons.solidSnake,
            title: 'Channel',
            values: [subscription.channelID, subscription.channelInternalName],
            mainAction: () => Navi.push(context, () => ChannelViewPage(channelID: subscription.channelID, preloadedData: null, needsReload: null)),
          );
        }
      },
    );
  }

  Widget _buildStatusCard(BuildContext context) {
    final acc = Provider.of<AppAuth>(context, listen: false);

    final item = subscription!;

    final isOwned = item.channelOwnerUserID == acc.userID && item.subscriberUserID == acc.userID;
    final isIncoming = item.channelOwnerUserID == acc.userID && item.subscriberUserID != acc.userID;
    final isOutgoing = item.channelOwnerUserID != acc.userID && item.subscriberUserID == acc.userID;

    var status = ['ERROR?'];

    if (isOutgoing && !item.confirmed) status = ['Subscription to foreign channel', 'Pending confirmation'];
    if (isOutgoing && !item.active) status = ['Subscription to foreign channel', 'Confirmed but inactive'];
    if (isOutgoing && item.active) status = ['Subscription to foreign channel', 'Confirmed and active'];

    if (isIncoming && !item.confirmed) status = ['External subscription to your channel', 'Pending confirmation'];
    if (isIncoming && !item.active) status = ['External subscription to your channel', 'Deactivated by subscriber'];
    if (isIncoming && item.active) status = ['External subscription to your channel', 'Confirmed and active'];

    if (isOwned && !item.confirmed) status = ['Your own channel', 'ERROR'];
    if (isOwned && !item.active) status = ['Your own channel', 'Not subscribed'];
    if (isOwned && item.active) status = ['Your own channel', 'Active subscription'];

    return UI.metaCard(
      context: context,
      icon: FontAwesomeIcons.solidInfo,
      title: 'Status',
      values: status,
    );
  }

  Future<UserPreview> _getUserPreview(AppAuth auth, String uid) 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, uid);

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

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

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

  void _confirm() async {
    final acc = AppAuth();

    if (subscription == null) return;

    try {
      await APIClient.confirmSubscription(acc, subscription!.channelID, subscription!.subscriptionID);
      widget.needsReload?.call();

      await _initStateAsync(false);

      Toaster.success("Success", 'Subscription succesfully confirmed');
    } catch (exc, trace) {
      Toaster.error("Error", 'Failed to confirm subscription');
      ApplicationLog.error('Failed to confirm subscription: ' + exc.toString(), trace: trace);
    }
  }

  void _unsubscribe({String? confirm = null}) async {
    final acc = AppAuth();

    if (subscription == null) return;

    if (confirm != null) {
      final r = await UIDialogs.showConfirmDialog(context, confirm, okText: 'Unsubscribe', cancelText: 'Cancel');
      if (!r) return;
    }

    try {
      await APIClient.deleteSubscription(acc, subscription!.channelID, subscription!.subscriptionID);
      widget.needsReload?.call();

      Toaster.success("Success", 'Unsubscribed from channel');
      Navi.pop(context);
    } catch (exc, trace) {
      Toaster.error("Error", 'Failed to unsubscribe from channel');
      ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
    }
  }

  void _deactivate() async {
    final acc = AppAuth();

    if (subscription == null) return;

    try {
      await APIClient.deactivateSubscription(acc, subscription!.channelID, subscription!.subscriptionID);
      widget.needsReload?.call();

      await _initStateAsync(false);

      Toaster.success("Success", 'Unsubscribed from channel');
    } catch (exc, trace) {
      Toaster.error("Error", 'Failed to unsubscribe from channel');
      ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
    }
  }

  void _activate() async {
    final acc = AppAuth();

    if (subscription == null) return;

    try {
      await APIClient.activateSubscription(acc, subscription!.channelID, subscription!.subscriptionID);
      widget.needsReload?.call();

      await _initStateAsync(false);

      Toaster.success("Success", 'Subscribed to channel');
    } catch (exc, trace) {
      Toaster.error("Error", 'Failed to subscribe to channel');
      ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
    }
  }
}