import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:material_symbols_icons/symbols.dart';

import '../focus/dpad_navigator.dart';
import '../i18n/strings.g.dart';
import '../mixins/mounted_set_state_mixin.dart';
import '../utils/platform_detector.dart';

Future<void> showTvVirtualKeyboard({
  required BuildContext context,
  required TextEditingController controller,
  String? hintText,
  TextInputType? keyboardType,
  TextInputAction? textInputAction,
  List<TextInputFormatter>? inputFormatters,
  bool obscureText = false,
  int? maxLength,
  int? maxLines,
  ValueChanged<String>? onChanged,
  ValueChanged<String>? onSubmitted,
  VoidCallback? onAction,
}) {
  if (!PlatformDetector.isAppleTV()) return Future.value();

  return showDialog<void>(
    context: context,
    barrierDismissible: true,
    builder: (context) => _TvVirtualKeyboardDialog(
      controller: controller,
      hintText: hintText,
      keyboardType: keyboardType,
      textInputAction: textInputAction,
      inputFormatters: inputFormatters,
      obscureText: obscureText,
      maxLength: maxLength,
      maxLines: maxLines,
      onChanged: onChanged,
      onSubmitted: onSubmitted,
      onAction: onAction,
    ),
  );
}

enum _TvKeyType { spacer, character, shift, space, newline, backspace, clear, cancel, done }

class _TvKey {
  final String label;
  final String value;
  final _TvKeyType type;
  final IconData? icon;

  const _TvKey.spacer() : label = '', value = '', type = _TvKeyType.spacer, icon = null;
  const _TvKey.character(this.value) : label = value, type = _TvKeyType.character, icon = null;
  const _TvKey.action(this.label, this.type, {this.icon}) : value = '';
}

class _TvVirtualKeyboardDialog extends StatefulWidget {
  final TextEditingController controller;
  final String? hintText;
  final TextInputType? keyboardType;
  final TextInputAction? textInputAction;
  final List<TextInputFormatter>? inputFormatters;
  final bool obscureText;
  final int? maxLength;
  final int? maxLines;
  final ValueChanged<String>? onChanged;
  final ValueChanged<String>? onSubmitted;
  final VoidCallback? onAction;

  const _TvVirtualKeyboardDialog({
    required this.controller,
    this.hintText,
    this.keyboardType,
    this.textInputAction,
    this.inputFormatters,
    this.obscureText = false,
    this.maxLength,
    this.maxLines,
    this.onChanged,
    this.onSubmitted,
    this.onAction,
  });

  @override
  State<_TvVirtualKeyboardDialog> createState() => _TvVirtualKeyboardDialogState();
}

class _TvVirtualKeyboardDialogState extends State<_TvVirtualKeyboardDialog> with MountedSetStateMixin {
  static const double _keySize = 60;
  static const double _keyGap = 6;
  static const double _rowGap = 6;

  final _focusNode = FocusNode(debugLabel: 'TvVirtualKeyboard');
  int _row = 0;
  int _column = 0;
  bool _shiftEnabled = false;

  List<List<_TvKey>> get _rows => _buildRows();

  @override
  void initState() {
    super.initState();
    _column = _firstFocusableColumn(_row) ?? 0;
    widget.controller.addListener(_handleTextChanged);
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (mounted) _focusNode.requestFocus();
    });
  }

  @override
  void dispose() {
    widget.controller.removeListener(_handleTextChanged);
    _focusNode.dispose();
    super.dispose();
  }

  void _handleTextChanged() {
    setStateIfMounted(() {});
  }

  bool get _isNumberKeyboard {
    final type = widget.keyboardType;
    return type?.index == TextInputType.number.index || type?.index == TextInputType.phone.index;
  }

  bool get _isMultiline {
    final type = widget.keyboardType;
    return type?.index == TextInputType.multiline.index || (widget.maxLines != null && widget.maxLines != 1);
  }

  List<List<_TvKey>> _buildRows() {
    if (_isNumberKeyboard) {
      return [
        _characters('123'),
        _characters('456'),
        _characters('789'),
        [
          _TvKey.action(t.common.clear, _TvKeyType.clear, icon: Icons.clear_all_rounded),
          const _TvKey.character('0'),
          const _TvKey.action('Del', _TvKeyType.backspace, icon: Icons.backspace_outlined),
        ],
        [
          _TvKey.action(t.common.cancel, _TvKeyType.cancel, icon: Icons.close_rounded),
          const _TvKey.character('.'),
          _TvKey.action(_doneLabel(), _TvKeyType.done, icon: _doneIcon()),
        ],
      ];
    }

    final actionRow = [
      const _TvKey.action('Space', _TvKeyType.space, icon: Icons.space_bar_rounded),
      const _TvKey.character('@'),
      const _TvKey.character('#'),
      const _TvKey.character('_'),
      const _TvKey.character('/'),
      const _TvKey.character(':'),
      _isMultiline
          ? const _TvKey.action('Line', _TvKeyType.newline, icon: Icons.keyboard_return_rounded)
          : const _TvKey.character('&'),
      _TvKey.action(t.common.clear, _TvKeyType.clear, icon: Icons.clear_all_rounded),
      _TvKey.action(t.common.cancel, _TvKeyType.cancel, icon: Icons.close_rounded),
      _TvKey.action(_doneLabel(), _TvKeyType.done, icon: _doneIcon()),
    ];

    return [
      [const _TvKey.spacer(), ..._characters('1234567890')],
      [const _TvKey.spacer(), ..._characters('qwertyuiop')],
      [const _TvKey.spacer(), ..._characters('asdfghjkl'), const _TvKey.character("'")],
      [
        _TvKey.action('Shift', _TvKeyType.shift, icon: Symbols.shift),
        ..._characters('zxcvbnm.-'),
        const _TvKey.action('Del', _TvKeyType.backspace, icon: Icons.backspace_outlined),
      ],
      [const _TvKey.spacer(), ...actionRow],
    ];
  }

  List<_TvKey> _characters(String chars) {
    return chars
        .split('')
        .map((c) {
          final code = c.codeUnitAt(0);
          final shifted = _shiftEnabled && code >= 0x61 && code <= 0x7a ? c.toUpperCase() : c;
          return _TvKey.character(shifted);
        })
        .toList(growable: false);
  }

  String _doneLabel() {
    switch (widget.textInputAction) {
      case TextInputAction.search:
        return t.common.search;
      case TextInputAction.next:
        return t.companionRemote.remote.next;
      case TextInputAction.go:
        return t.common.submit;
      default:
        return t.common.ok;
    }
  }

  IconData _doneIcon() {
    switch (widget.textInputAction) {
      case TextInputAction.search:
        return Icons.search_rounded;
      case TextInputAction.next:
        return Icons.arrow_forward_rounded;
      case TextInputAction.go:
        return Icons.keyboard_double_arrow_right_rounded;
      default:
        return Icons.check_rounded;
    }
  }

  KeyEventResult _handleKey(FocusNode _, KeyEvent event) {
    final key = event.logicalKey;

    if (key.isBackKey) {
      if (event is KeyDownEvent) Navigator.of(context).pop();
      return KeyEventResult.handled;
    }

    if (event is KeyDownEvent || event is KeyRepeatEvent) {
      if (key == LogicalKeyboardKey.backspace || key == LogicalKeyboardKey.delete) {
        _backspace();
        return KeyEventResult.handled;
      }

      if (event.isPhysicalKeyboardEnter) {
        if (_isMultiline) {
          _insert('\n');
        } else if (event is KeyDownEvent) {
          _submit();
        }
        return KeyEventResult.handled;
      }

      if (event.isTvSelectEvent) {
        _activate(_rows[_row][_column]);
        return KeyEventResult.handled;
      }

      if (key.isUpKey) {
        _moveVertical(-1);
        return KeyEventResult.handled;
      }
      if (key.isDownKey) {
        _moveVertical(1);
        return KeyEventResult.handled;
      }
      if (key.isLeftKey) {
        _moveHorizontal(-1);
        return KeyEventResult.handled;
      }
      if (key.isRightKey) {
        _moveHorizontal(1);
        return KeyEventResult.handled;
      }

      final character = event.character;
      if (character != null && character.isNotEmpty && !key.isNavigationKey) {
        _insert(character);
        return KeyEventResult.handled;
      }
    }

    return KeyEventResult.handled;
  }

  void _moveHorizontal(int delta) {
    final nextColumn = _nextFocusableColumn(_row, _column + delta, delta);
    if (nextColumn == null) return;
    setState(() {
      _column = nextColumn;
    });
  }

  void _moveVertical(int delta) {
    final rows = _rows;
    final nextRow = (_row + delta).clamp(0, rows.length - 1).toInt();
    final nextColumn = _nearestFocusableColumn(nextRow, _column);
    setState(() {
      _row = nextRow;
      _column = nextColumn;
    });
  }

  int? _firstFocusableColumn(int row) {
    final rows = _rows;
    if (row < 0 || row >= rows.length) return null;
    final index = rows[row].indexWhere(_isFocusableKey);
    return index == -1 ? null : index;
  }

  int? _nextFocusableColumn(int row, int column, int delta) {
    final rows = _rows;
    if (row < 0 || row >= rows.length) return null;
    for (var c = column; c >= 0 && c < rows[row].length; c += delta) {
      if (_isFocusableKey(rows[row][c])) return c;
    }
    return null;
  }

  int _nearestFocusableColumn(int row, int preferredColumn) {
    final rows = _rows;
    if (row < 0 || row >= rows.length) return preferredColumn;
    final keys = rows[row];
    if (preferredColumn >= 0 && preferredColumn < keys.length && _isFocusableKey(keys[preferredColumn])) {
      return preferredColumn;
    }
    for (var offset = 1; offset < keys.length; offset++) {
      final right = preferredColumn + offset;
      if (right >= 0 && right < keys.length && _isFocusableKey(keys[right])) return right;
      final left = preferredColumn - offset;
      if (left >= 0 && left < keys.length && _isFocusableKey(keys[left])) return left;
    }
    return _firstFocusableColumn(row) ?? 0;
  }

  bool _isFocusableKey(_TvKey key) => key.type != _TvKeyType.spacer;

  void _activate(_TvKey key) {
    switch (key.type) {
      case _TvKeyType.spacer:
        return;
      case _TvKeyType.character:
        _insert(key.value);
        return;
      case _TvKeyType.shift:
        setState(() => _shiftEnabled = !_shiftEnabled);
        return;
      case _TvKeyType.space:
        _insert(' ');
        return;
      case _TvKeyType.newline:
        _insert('\n');
        return;
      case _TvKeyType.backspace:
        _backspace();
        return;
      case _TvKeyType.clear:
        _replace(TextEditingValue.empty);
        return;
      case _TvKeyType.cancel:
        Navigator.of(context).pop();
        return;
      case _TvKeyType.done:
        _submit();
        return;
    }
  }

  void _submit() {
    final text = widget.controller.text;
    final onSubmitted = widget.onSubmitted;
    final onAction = widget.onAction;
    Navigator.of(context).pop();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (onSubmitted != null) {
        onSubmitted(text);
      } else {
        onAction?.call();
      }
    });
  }

  void _insert(String text) {
    final value = widget.controller.value;
    final selection = value.selection;
    final start = selection.isValid
        ? (selection.start < selection.end ? selection.start : selection.end)
        : value.text.length;
    final end = selection.isValid
        ? (selection.start > selection.end ? selection.start : selection.end)
        : value.text.length;
    final newText = value.text.replaceRange(start, end, text);
    _replace(
      value.copyWith(
        text: newText,
        selection: TextSelection.collapsed(offset: start + text.length),
        composing: TextRange.empty,
      ),
    );
  }

  void _backspace() {
    final value = widget.controller.value;
    final selection = value.selection;
    final start = selection.isValid
        ? (selection.start < selection.end ? selection.start : selection.end)
        : value.text.length;
    final end = selection.isValid
        ? (selection.start > selection.end ? selection.start : selection.end)
        : value.text.length;

    if (start != end) {
      _replace(
        value.copyWith(
          text: value.text.replaceRange(start, end, ''),
          selection: TextSelection.collapsed(offset: start),
        ),
      );
      return;
    }
    if (start == 0) return;

    _replace(
      value.copyWith(
        text: value.text.replaceRange(start - 1, start, ''),
        selection: TextSelection.collapsed(offset: start - 1),
        composing: TextRange.empty,
      ),
    );
  }

  void _replace(TextEditingValue nextValue) {
    final previousValue = widget.controller.value;
    var formattedValue = nextValue;
    final maxLength = widget.maxLength;
    final formatters = [
      ...?widget.inputFormatters,
      if (maxLength != null && maxLength > 0) LengthLimitingTextInputFormatter(maxLength),
    ];
    for (final formatter in formatters) {
      formattedValue = formatter.formatEditUpdate(previousValue, formattedValue);
    }
    widget.controller.value = formattedValue;
    widget.onChanged?.call(formattedValue.text);
  }

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;
    final text = widget.controller.text;

    return Dialog(
      insetPadding: const EdgeInsets.symmetric(horizontal: 56, vertical: 32),
      backgroundColor: Colors.transparent,
      child: Focus(
        focusNode: _focusNode,
        onKeyEvent: _handleKey,
        child: Container(
          constraints: const BoxConstraints(maxWidth: 860),
          padding: const EdgeInsets.all(14),
          decoration: BoxDecoration(
            color: colorScheme.surface.withValues(alpha: 0.96),
            borderRadius: BorderRadius.circular(28),
          ),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              _buildPreview(context, text),
              const SizedBox(height: 12),
              for (var row = 0; row < _rows.length; row++) ...[
                _buildRow(context, row),
                if (row != _rows.length - 1) const SizedBox(height: _rowGap),
              ],
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildPreview(BuildContext context, String text) {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;
    final isEmpty = text.isEmpty;
    final displayText = widget.obscureText && !isEmpty ? List.filled(text.length, '*').join() : text;
    final previewText = isEmpty ? (widget.hintText ?? '') : displayText;
    final multiline = _isMultiline;

    return Container(
      height: multiline ? 86 : 60,
      alignment: Alignment.centerLeft,
      padding: const EdgeInsets.symmetric(horizontal: 16),
      decoration: BoxDecoration(
        color: colorScheme.surfaceContainerHighest.withValues(alpha: 0.7),
        borderRadius: BorderRadius.circular(18),
      ),
      child: multiline
          ? SingleChildScrollView(
              reverse: true,
              child: Text(
                previewText,
                maxLines: 3,
                style: theme.textTheme.titleLarge?.copyWith(
                  color: isEmpty ? colorScheme.onSurfaceVariant : colorScheme.onSurface,
                ),
              ),
            )
          : SingleChildScrollView(
              scrollDirection: Axis.horizontal,
              reverse: true,
              child: Text(
                previewText,
                maxLines: 1,
                style: theme.textTheme.headlineSmall?.copyWith(
                  color: isEmpty ? colorScheme.onSurfaceVariant : colorScheme.onSurface,
                ),
              ),
            ),
    );
  }

  Widget _buildRow(BuildContext context, int row) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        for (var column = 0; column < _rows[row].length; column++) ...[
          _buildKey(context, _rows[row][column], row, column),
          if (column != _rows[row].length - 1) const SizedBox(width: _keyGap),
        ],
      ],
    );
  }

  Widget _buildKey(BuildContext context, _TvKey key, int row, int column) {
    if (key.type == _TvKeyType.spacer) {
      return const SizedBox(width: _keySize, height: _keySize);
    }

    final colorScheme = Theme.of(context).colorScheme;
    final selected = row == _row && column == _column;
    final active = key.type == _TvKeyType.shift && _shiftEnabled;
    final background = selected
        ? colorScheme.primary
        : active
        ? colorScheme.secondaryContainer
        : colorScheme.surfaceContainerHighest.withValues(alpha: 0.88);
    final foreground = selected
        ? colorScheme.onPrimary
        : active
        ? colorScheme.onSecondaryContainer
        : colorScheme.onSurface;

    return GestureDetector(
      onTap: () {
        setState(() {
          _row = row;
          _column = column;
        });
        _activate(key);
      },
      child: AnimatedContainer(
        duration: const Duration(milliseconds: 120),
        width: _keySize,
        height: _keySize,
        alignment: Alignment.center,
        decoration: BoxDecoration(color: background, borderRadius: BorderRadius.circular(16)),
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 4),
          child: _buildKeyContent(context, key, foreground),
        ),
      ),
    );
  }

  Widget _buildKeyContent(BuildContext context, _TvKey key, Color foreground) {
    final icon = key.icon;
    if (icon != null) {
      return Icon(icon, color: foreground, size: key.type == _TvKeyType.space ? 34 : 30);
    }

    return FittedBox(
      fit: BoxFit.scaleDown,
      child: Text(
        key.label,
        maxLines: 1,
        style: Theme.of(context).textTheme.titleLarge?.copyWith(color: foreground, fontWeight: FontWeight.w800),
      ),
    );
  }
}
