import 'package:flutter/widgets.dart';

/// 데스크톱 레이아웃으로 간주할 최소 너비(px).
const double desktopBreakpoint = 1200;

/// 태블릿 레이아웃을 구분하는 최소 너비(px).
const double tabletBreakpoint = 960;

/// 뷰포트 크기별 분기값.
enum DeviceBreakpoint { mobile, tablet, desktop }

/// 현재 화면 너비에 맞는 [DeviceBreakpoint]를 계산한다.
DeviceBreakpoint breakpointForWidth(double width) {
  if (width >= desktopBreakpoint) {
    return DeviceBreakpoint.desktop;
  }
  if (width >= tabletBreakpoint) {
    return DeviceBreakpoint.tablet;
  }
  return DeviceBreakpoint.mobile;
}

/// 주어진 너비가 데스크톱 분기에 해당하는지 여부.
bool isDesktop(double width) => width >= desktopBreakpoint;

/// 주어진 너비가 태블릿 분기에 해당하는지 여부.
bool isTablet(double width) =>
    width >= tabletBreakpoint && width < desktopBreakpoint;

/// 주어진 너비가 모바일 분기에 해당하는지 여부.
bool isMobile(double width) => width < tabletBreakpoint;

/// 컨텍스트 기반으로 데스크톱 범위인지 확인한다.
bool isDesktopContext(BuildContext context) =>
    isDesktop(MediaQuery.of(context).size.width);

/// 컨텍스트 기반으로 태블릿 범위인지 확인한다.
bool isTabletContext(BuildContext context) =>
    isTablet(MediaQuery.of(context).size.width);

/// 컨텍스트 기반으로 모바일 범위인지 확인한다.
bool isMobileContext(BuildContext context) =>
    isMobile(MediaQuery.of(context).size.width);

/// 반응형 분기 정보를 담는 값 객체.
class ResponsiveBreakpoints {
  ResponsiveBreakpoints._(this.width) : breakpoint = breakpointForWidth(width);

  /// 현재 뷰 가로 너비.
  final double width;

  /// 너비에서 계산된 분기값.
  final DeviceBreakpoint breakpoint;

  /// 모바일 범위인지 여부.
  bool get isMobile => breakpoint == DeviceBreakpoint.mobile;

  /// 태블릿 범위인지 여부.
  bool get isTablet => breakpoint == DeviceBreakpoint.tablet;

  /// 데스크톱 범위인지 여부.
  bool get isDesktop => breakpoint == DeviceBreakpoint.desktop;

  /// 현재 컨텍스트에서 [ResponsiveBreakpoints]를 생성한다.
  static ResponsiveBreakpoints of(BuildContext context) {
    final size = MediaQuery.of(context).size;
    return ResponsiveBreakpoints._(size.width);
  }
}

/// 기기 타입에 따라 위젯 빌더를 분기 실행하는 레이아웃 헬퍼.
class ResponsiveLayoutBuilder extends StatelessWidget {
  const ResponsiveLayoutBuilder({
    super.key,
    required this.mobile,
    this.tablet,
    required this.desktop,
  });

  /// 모바일 뷰에서 사용할 빌더.
  final WidgetBuilder mobile;

  /// 태블릿 뷰에서 사용할 빌더. 제공되지 않으면 데스크톱 빌더를 재사용한다.
  final WidgetBuilder? tablet;

  /// 데스크톱 뷰에서 사용할 빌더.
  final WidgetBuilder desktop;

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final breakpoint = breakpointForWidth(constraints.maxWidth);
        switch (breakpoint) {
          case DeviceBreakpoint.mobile:
            return mobile(context);
          case DeviceBreakpoint.tablet:
            final tabletBuilder = tablet ?? desktop;
            return tabletBuilder(context);
          case DeviceBreakpoint.desktop:
            return desktop(context);
        }
      },
    );
  }
}

/// 특정 분기에서만 child를 표시하는 헬퍼 위젯.
class ResponsiveVisibility extends StatelessWidget {
  const ResponsiveVisibility({
    super.key,
    required this.child,
    this.replacement = const SizedBox.shrink(),
    this.visibleOn = const {
      DeviceBreakpoint.mobile,
      DeviceBreakpoint.tablet,
      DeviceBreakpoint.desktop,
    },
  });

  /// 조건을 만족할 때 보여줄 실제 위젯.
  final Widget child;

  /// 조건을 만족하지 않을 때 대체로 렌더링할 위젯.
  final Widget replacement;

  /// 어떤 분기에서 child를 노출할지 정의한 집합.
  final Set<DeviceBreakpoint> visibleOn;

  @override
  Widget build(BuildContext context) {
    final breakpoint = ResponsiveBreakpoints.of(context).breakpoint;
    return visibleOn.contains(breakpoint) ? child : replacement;
  }
}
