import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';

import '../../about/external/data/Commitment.dart';
import '../../about/external/initial/CommitmentRequest.dart';
import '../../about/internal/application/CommitmentClass.dart';
import '../../about/internal/application/EnergyCost.dart';
import '../../about/internal/application/NotificationType.dart';
import '../../about/internal/application/ProofType.dart';
import '../../about/internal/application/TextType.dart';
import '../../designs/Component.dart';
import '../../designs/Responsive.dart';
import '../../designs/Shell.dart';
import '../../designs/buttons/Buttons.dart';
import '../../designs/input/InputFields.dart';
import '../../designs/text/Text.dart';
import '../../utils/CapacityEngine.dart';
import '../../utils/Colors.dart';
import '../../utils/CommonUtils.dart';
import '../../utils/Validators.dart';
import 'ConnectNewCommitment.dart';
import 'NewCommitment.dart';
import 'ViewNewCommitment.dart';

class NewCommitmentState extends State<NewCommitment>
    implements ConnectNewCommitment {
  ViewNewCommitment? _model;

  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();

  final TextEditingController _title = TextEditingController();

  final TextEditingController _category = TextEditingController();

  final TextEditingController _estimate = TextEditingController();

  CommitmentClass _class = CommitmentClass.Standard;

  EnergyCost _energy = EnergyCost.Medium;

  ProofType _proof = ProofType.Honour;

  DateTime? _windowStart;

  DateTime? _windowEnd;

  double _multiplier = 1.0;

  String _windowError = "";

  @override
  Widget build(BuildContext context) {
    return ViewModelBuilder<ViewNewCommitment>.reactive(
      viewModelBuilder: () => ViewNewCommitment(context, this),
      onViewModelReady: (viewModel) {
        _model = viewModel;
        _initiate();
      },
      builder: (context, viewModel, child) => LayoutBuilder(
        builder: (BuildContext context, BoxConstraints constraints) {
          return Responsive(
            mobile: _mobileView(constraints),
            tablet: _mobileView(constraints),
            desktop: _mobileView(constraints),
          );
        },
      ),
    );
  }

  void _initiate() {
    // A window, not a date: the default opens now and closes in two hours, so
    // the field is never left as a bare day.
    final DateTime now = DateTime.now();
    setState(() {
      _windowStart = now;
      _windowEnd = now.add(const Duration(hours: 2));
    });
  }

  // ── Handlers ──────────────────────────────────────────────────────────────

  void _onBack() {
    Navigator.pop(context, false);
  }

  void _onCategoryChanged(String value) {
    _model?.resolveMultiplier(value.trim());
  }

  void _onClassSelected(CommitmentClass value) {
    setState(() {
      _class = value;
      // Non-negotiables carry real consequences, so they default to real proof
      // rather than the honour checkbox.
      if (value == CommitmentClass.NonNegotiable &&
          _proof == ProofType.Honour) {
        _proof = ProofType.Photo;
      }
    });
  }

  void _onEnergySelected(EnergyCost value) {
    setState(() {
      _energy = value;
    });
  }

  void _onProofSelected(ProofType value) {
    setState(() {
      _proof = value;
    });
  }

  void _onPickWindowStart() async {
    final DateTime? picked = await _pickMoment(_windowStart);
    if (picked == null) {
      return;
    }

    setState(() {
      _windowStart = picked;
      if (_windowEnd == null || !_windowEnd!.isAfter(picked)) {
        _windowEnd = picked.add(const Duration(hours: 2));
      }
      _windowError = "";
    });
  }

  void _onPickWindowEnd() async {
    final DateTime? picked = await _pickMoment(_windowEnd);
    if (picked == null) {
      return;
    }

    setState(() {
      _windowEnd = picked;
      _windowError = Validators.window(_windowStart, _windowEnd) ?? "";
    });
  }

  Future<DateTime?> _pickMoment(DateTime? initial) async {
    final DateTime base = initial ?? DateTime.now();

    final DateTime? day = await showDatePicker(
      context: context,
      initialDate: base,
      firstDate: DateTime.now().subtract(const Duration(days: 1)),
      lastDate: DateTime.now().add(const Duration(days: 365)),
    );

    if (day == null || !mounted) {
      return null;
    }

    final TimeOfDay? time = await showTimePicker(
      context: context,
      initialTime: TimeOfDay.fromDateTime(base),
    );

    if (time == null) {
      return null;
    }

    return DateTime(day.year, day.month, day.day, time.hour, time.minute);
  }

  void _onSave() {
    final String? windowIssue = Validators.window(_windowStart, _windowEnd);

    if (windowIssue != null) {
      setState(() {
        _windowError = windowIssue;
      });
      return;
    }

    if (_formKey.currentState?.validate() != true) {
      return;
    }

    _model?.save(_buildRequest(), _buildCandidate());
  }

  CommitmentRequest _buildRequest() {
    return CommitmentRequest(
      commitmentClass: _class.name,
      title: _title.text.trim(),
      category: _category.text.trim(),
      dueStart: _windowStart?.toIso8601String() ?? "",
      dueEnd: _windowEnd?.toIso8601String() ?? "",
      estMinutes: int.tryParse(_estimate.text.trim()) ?? 0,
      energy: _energy.name,
      proofType: _proof.name,
      proofTimerMinutes:
          _proof == ProofType.Timer ? int.tryParse(_estimate.text.trim()) ?? 0 : 0,
    );
  }

  Commitment _buildCandidate() {
    return Commitment(
      commitmentClass: _class,
      title: _title.text.trim(),
      category: _category.text.trim(),
      dueStart: _windowStart,
      dueEnd: _windowEnd,
      estMinutes: int.tryParse(_estimate.text.trim()) ?? 0,
      energy: _energy,
      proofType: _proof,
    );
  }

  // ── Views ─────────────────────────────────────────────────────────────────

  Widget _mobileView(BoxConstraints constraints) {
    final int estimate = int.tryParse(_estimate.text.trim()) ?? 0;

    final bool multiplierWorthShowing = _multiplier > 1.15 && estimate > 0;

    return Sheet(
      eyebrow: "New",
      title: "Commitment",
      onBack: _onBack,
      child: Form(
        key: _formKey,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          mainAxisSize: MainAxisSize.min,
          children: [
            displayTitle("What are you\ncommitting to?"),
            const SizedBox(height: 28),
            inputField(
              "Title",
              _title,
              hint: "Say it the way you would say it out loud",
              validator: Validators.title,
            ),
            const SizedBox(height: 20),
            inputField(
              "Category",
              _category,
              hint: "Admin, gym, deep work…",
              onChanged: _onCategoryChanged,
            ),
            const SizedBox(height: 28),
            sectionBreak("Class", caption: "decides the weight"),
            segmentedSelector<CommitmentClass>(
              options: CommitmentClass.values,
              selected: _class,
              label: classLabel,
              onSelected: _onClassSelected,
              activeColor: classColor(_class),
            ),
            const SizedBox(height: 12),
            text(
              _classDescription(_class),
              12,
              TextType.Regular,
              color: colorGrey2,
              height: 1.5,
            ),
            const SizedBox(height: 28),
            sectionBreak("Window", caption: "not just a day"),
            if (_windowError.isNotEmpty) ...[
              text(_windowError, 11, TextType.Regular, color: colorNegative),
              const SizedBox(height: 10),
            ],
            Row(
              children: [
                Expanded(
                  child: selectField(
                    "Opens",
                    _windowStart == null ? "" : formatDateTime(_windowStart),
                    _onPickWindowStart,
                    icon: CupertinoIcons.calendar,
                  ),
                ),
                const SizedBox(width: 10),
                Expanded(
                  child: selectField(
                    "Closes",
                    _windowEnd == null ? "" : formatDateTime(_windowEnd),
                    _onPickWindowEnd,
                    icon: CupertinoIcons.calendar,
                  ),
                ),
              ],
            ),
            const SizedBox(height: 10),
            text(
              "When this window closes, the item goes overdue. It does not roll over to tomorrow.",
              12,
              TextType.Regular,
              color: colorGrey2,
              height: 1.5,
            ),
            const SizedBox(height: 28),
            sectionBreak("Effort"),
            inputField(
              "Estimated minutes",
              _estimate,
              hint: "How long you think it takes",
              validator: Validators.estimateMinutes,
              keyboard: TextInputType.number,
              onChanged: (value) => setState(() {}),
            ),
            if (multiplierWorthShowing) ...[
              const SizedBox(height: 12),
              card(
                background: colorStandingWarnedBg,
                borderColor: colorStandingWarned.withValues(alpha: 0.20),
                padding: const EdgeInsets.all(14),
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Icon(CupertinoIcons.info_circle_fill,
                        size: 15, color: colorStandingWarned),
                    const SizedBox(width: 10),
                    Expanded(
                      child: text(
                        "On ${_category.text.trim().isEmpty ? "this category" : _category.text.trim()} you historically take ${_multiplier.toStringAsFixed(1)}× your estimate. Your day is planned against ${formatMinutes(estimate * _multiplier)}.",
                        12,
                        TextType.Regular,
                        color: colorPrimaryDark,
                        height: 1.5,
                      ),
                    ),
                  ],
                ),
              ),
            ],
            const SizedBox(height: 20),
            text("ENERGY COST", 9, TextType.Bold,
                color: colorGrey2, letterSpacing: 1.0),
            const SizedBox(height: 10),
            segmentedSelector<EnergyCost>(
              options: EnergyCost.values,
              selected: _energy,
              label: (value) => value.name,
              onSelected: _onEnergySelected,
            ),
            const SizedBox(height: 28),
            sectionBreak("Proof", caption: "the checkbox is the enemy"),
            segmentedSelector<ProofType>(
              options: ProofType.values,
              selected: _proof,
              label: proofLabel,
              onSelected: _onProofSelected,
            ),
            const SizedBox(height: 12),
            text(
              _proofDescription(_proof),
              12,
              TextType.Regular,
              color: colorGrey2,
              height: 1.5,
            ),
            const SizedBox(height: 32),
            roundedCornerButton(
              "Commit",
              _onSave,
              icon: CupertinoIcons.checkmark,
            ),
            const SizedBox(height: 10),
            Center(
              child: text(
                "You can change the details later. You cannot change the history.",
                11,
                TextType.Regular,
                color: colorGrey2,
                align: TextAlign.center,
              ),
            ),
          ],
        ),
      ),
    );
  }

  String _classDescription(CommitmentClass value) {
    switch (value) {
      case CommitmentClass.NonNegotiable:
        return "Never deferrable, heaviest debt, hardest escalation. Meds, deadlines, rent.";
      case CommitmentClass.Standard:
        return "Normal weight, two deferrals, then it is complete or abandon.";
      case CommitmentClass.Elective:
        return "No debt if you miss it. Auto-archives if it sits untouched.";
    }
  }

  String _proofDescription(ProofType value) {
    switch (value) {
      case ProofType.Honour:
        return "A plain checkbox. Fine for trivia, worthless for anything you actually lie to yourself about.";
      case ProofType.Photo:
        return "Camera only, no gallery. Timestamped, and near-duplicate photos get flagged.";
      case ProofType.Timer:
        return "A foreground session. Backgrounding the app pauses the clock.";
      case ProofType.Location:
        return "Geofence dwell. Being near it does not count as being there.";
      case ProofType.Health:
        return "Your health platform confirms the workout happened inside the window.";
      case ProofType.Witness:
        return "Someone else confirms it. The hardest one to talk your way around.";
    }
  }

  /// The capacity refusal. It shows the arithmetic rather than just saying no,
  /// because the point is to make overcommitment visible.
  void _openCapacitySheet(CapacityVerdict verdict) {
    showModalBottomSheet(
      context: context,
      isScrollControlled: true,
      backgroundColor: Colors.transparent,
      barrierColor: colorPrimaryDark.withValues(alpha: 0.6),
      builder: (BuildContext sheetContext) {
        return Container(
          decoration: BoxDecoration(
            color: colorSheetBackground,
            borderRadius: const BorderRadius.only(
              topLeft: Radius.circular(28),
              topRight: Radius.circular(28),
            ),
          ),
          child: SingleChildScrollView(
            padding: const EdgeInsets.fromLTRB(24, 16, 24, 32),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                Center(
                  child: Container(
                    width: 36,
                    height: 4,
                    margin: const EdgeInsets.only(bottom: 20),
                    decoration: BoxDecoration(
                      color: colorGrey.withValues(alpha: 0.35),
                      borderRadius: BorderRadius.circular(999),
                    ),
                  ),
                ),
                text("OVER CAPACITY", 9, TextType.Bold,
                    color: colorStandingWarned, letterSpacing: 1.2),
                const SizedBox(height: 10),
                text("This day is already full.", 26, TextType.Light,
                    color: colorPrimaryDark, height: 1.2),
                const SizedBox(height: 14),
                text(verdict.message, 14, TextType.Regular,
                    color: colorGrey2, height: 1.55),
                const SizedBox(height: 24),
                card(
                  child: Row(
                    children: [
                      Expanded(
                        child: labelled(
                          "Planned",
                          formatMinutes(verdict.projectedMinutes),
                          valueSize: 20,
                          valueType: TextType.Light,
                        ),
                      ),
                      Expanded(
                        child: labelled(
                          "You do",
                          formatMinutes(verdict.historicalMinutes),
                          valueSize: 20,
                          valueType: TextType.Light,
                        ),
                      ),
                      Expanded(
                        child: labelled(
                          "Cut",
                          formatMinutes(verdict.excessMinutes),
                          valueSize: 20,
                          valueType: TextType.Light,
                          valueColor: colorStandingGrounded,
                        ),
                      ),
                    ],
                  ),
                ),
                const SizedBox(height: 20),
                text(
                  "Chronic overdue is usually an overcommitment problem wearing a laziness costume. Cutting something now is the cheapest fix available.",
                  12,
                  TextType.Regular,
                  color: colorGrey2,
                  height: 1.5,
                ),
                const SizedBox(height: 24),
                roundedCornerButton(
                  "Let me cut something",
                  () => Navigator.pop(sheetContext),
                  icon: CupertinoIcons.scissors,
                ),
                const SizedBox(height: 8),
                Center(
                  child: textButton(
                    "Add it anyway",
                    () {
                      Navigator.pop(sheetContext);
                      _model?.saveAnyway(_buildRequest());
                    },
                    textSize: 12,
                    color: colorGrey2,
                  ),
                ),
              ],
            ),
          ),
        );
      },
    );
  }

  // ── ConnectNewCommitment ──────────────────────────────────────────────────

  @override
  void onSaved() {
    _model?.showApplicationNotification(
      NotificationType.success,
      "Committed",
      "It is on the record now. The window closes ${formatDateTime(_windowEnd)}.",
      true,
      true,
      () {
        Navigator.pop(context, true);
      },
    );
  }

  @override
  void onCapacityBlocked(CapacityVerdict verdict) {
    _openCapacitySheet(verdict);
  }

  @override
  void onMultiplierResolved(double multiplier) {
    setState(() {
      _multiplier = multiplier;
    });
  }

  @override
  void dispose() {
    _title.dispose();
    _category.dispose();
    _estimate.dispose();
    super.dispose();
  }
}
