from __future__ import annotations

import os
import shutil
import sys
import time
from textwrap import shorten, wrap

try:
    from colorama import Style
except Exception:  # pragma: no cover
    class _Style:
        BRIGHT = RESET_ALL = DIM = ''
    Style = _Style()

from .theme import Theme


DENSITY_SPACING = {
    'compact': 0,
    'cozy': 1,
}


def clear_screen() -> None:
    os.system('cls' if os.name == 'nt' else 'clear')


def term_size(default: tuple[int, int] = (112, 30)) -> tuple[int, int]:
    size = shutil.get_terminal_size(default)
    return size.columns, size.lines


def safe_width(minimum: int = 72, maximum: int = 132) -> int:
    width, _ = term_size()
    return min(max(width, minimum), maximum)


def color_line(text: str, color: str, bold: bool = False, dim: bool = False) -> str:
    prefix = color
    if bold:
        prefix += Style.BRIGHT
    if dim:
        prefix += getattr(Style, 'DIM', '')
    return prefix + str(text) + Style.RESET_ALL


def wrap_block(text: str, width: int | None = None) -> list[str]:
    width = width or max(safe_width() - 10, 28)
    lines: list[str] = []
    for raw in str(text).splitlines() or ['']:
        lines.extend(wrap(raw, width=width) or [''])
    return lines


def _fit(text: str, width: int) -> str:
    return shorten(str(text).replace('\n', ' '), width=max(8, width), placeholder='…')


def _rule(width: int, left: str, fill: str, right: str) -> str:
    return left + fill * max(0, width - 2) + right


def box(title: str, subtitle: str, theme: Theme) -> str:
    width = safe_width()
    inner = width - 2
    ribbon = f' ✦ {title} ✦ '
    title_line = ribbon.center(inner, '═')
    lines = [color_line(_rule(width, '╔', '═', '╗'), theme.border, bold=True)]
    lines.append(color_line('║' + title_line + '║', theme.title_glow or theme.primary, bold=True))
    if subtitle:
        lines.append(color_line('║' + _fit(subtitle.center(inner), inner).ljust(inner) + '║', theme.muted))
    lines.append(color_line(_rule(width, '╚', '═', '╝'), theme.border, bold=True))
    return '\n'.join(lines)


def panel(title: str, body_lines: list[str], theme: Theme, *, tone: str = 'border') -> str:
    width = safe_width()
    inner = width - 4
    border_color = getattr(theme, tone, theme.border) if hasattr(theme, tone) else theme.border
    lines = [color_line(_rule(width, '╭', '─', '╮'), border_color, bold=True)]
    lines.append(color_line(f'│ {title.ljust(inner)} │', theme.secondary, bold=True))
    lines.append(color_line(_rule(width, '├', '─', '┤'), border_color))
    for line in body_lines:
        for chunk in wrap_block(line, inner):
            lines.append(f'│ {chunk.ljust(inner)} │')
    lines.append(color_line(_rule(width, '╰', '─', '╯'), border_color, bold=True))
    return '\n'.join(lines)


def highlight(text: str, focused: bool, theme: Theme, accessibility_mode: bool = False) -> str:
    if focused:
        marker = '▶▶ ' if accessibility_mode else '❯ '
        return color_line(marker + text, theme.accent, bold=True)
    return '  ' + text


def divider(label: str = '', theme: Theme | None = None) -> str:
    width = safe_width() - 8
    label = f' {label} ' if label else ''
    left = '─' * max(0, (width - len(label)) // 2)
    right = '─' * max(0, width - len(label) - len(left))
    text = left + label + right
    if theme:
        return color_line(text, theme.muted)
    return text


def chip(text: str, theme: Theme, *, active: bool = False, tone: str = 'primary') -> str:
    color = getattr(theme, tone, theme.primary) if hasattr(theme, tone) else theme.primary
    left, right = ('⟦', '⟧') if active else ('〔', '〕')
    return color_line(f'{left}{text}{right}', color, bold=active)


def tab_strip(tabs: list[str], active_index: int, theme: Theme) -> str:
    rendered = []
    for idx, title in enumerate(tabs):
        label = f'{idx + 1}:{title}'
        rendered.append(chip(label, theme, active=idx == active_index, tone='accent' if idx == active_index else 'muted'))
    return ' '.join(rendered)


def section_header(title: str, theme: Theme, *, count: int = 0) -> str:
    suffix = f' • {count} widgets' if count else ''
    label = f' {title}{suffix} '
    width = safe_width() - 4
    text = '╼╼' + label + '╾' * max(2, width - len(label) - 2)
    return color_line(text[:width], theme.secondary, bold=True)


def stat_row(items: list[str], theme: Theme) -> str:
    return '  '.join(chip(item, theme, tone='muted') for item in items if item)


def widget_card(title: str, lines: list[str], theme: Theme, *, focused: bool = False, tone: str = 'primary', index: int | None = None) -> str:
    width = safe_width() - 2
    inner = width - 4
    border = getattr(theme, tone, theme.border) if hasattr(theme, tone) else theme.border
    bar = theme.accent if focused else border
    top_left = '┏' if focused else '┌'
    top_right = '┓' if focused else '┐'
    bottom_left = '┗' if focused else '└'
    bottom_right = '┛' if focused else '┘'
    left_side = '┃' if focused else '│'
    right_side = '┃' if focused else '│'
    title_prefix = f'{index:02d} ' if index is not None else ''
    heading = _fit(f'{title_prefix}{title}', inner)
    card = [color_line(top_left + '━' * (width - 2) + top_right, bar, bold=True)]
    card.append(color_line(f'{left_side} {heading.ljust(inner)} {right_side}', theme.title_glow or theme.primary, bold=True))
    card.append(color_line(f'{left_side} ' + ('·' * inner) + f' {right_side}', theme.muted, dim=True))
    for line in lines:
        chunks = wrap_block(line, inner) or ['']
        for chunk in chunks:
            card.append(f'{left_side} {chunk.ljust(inner)} {right_side}')
    card.append(color_line(bottom_left + '━' * (width - 2) + bottom_right, bar, bold=True))
    return '\n'.join(card)


def footer_bar(commands: list[str], theme: Theme) -> str:
    lead = color_line('⌘', theme.accent, bold=True)
    return lead + ' ' + ' '.join(chip(cmd, theme, tone='muted') for cmd in commands)


def spinner_frame(step: int) -> str:
    frames = ['⠁', '⠂', '⠄', '⠂'] if sys.stdout.encoding else ['-', '\\', '|', '/']
    return frames[step % len(frames)]


def animate_once(label: str, theme: Theme, duration: float = 0.35) -> None:
    start = time.time()
    step = 0
    while time.time() - start < duration:
        print('\r' + color_line(f'{spinner_frame(step)} {label}', theme.primary), end='', flush=True)
        time.sleep(0.08)
        step += 1
    print('\r' + ' ' * (len(label) + 4) + '\r', end='', flush=True)
