@use "sass:list";
@use "sass:math";
@use "sass:string";

// CSS-in-JS 스타일링 도구(styleRem/styleBaseSize/styleSpacingSize)를 Sass로 옮긴다.
// - styleRemAmount, styleBaseSize, styleSpacingSize와 동일한 계산 규칙을 유지한다.
// - @use alias(`as style`)를 붙이면 style.base-size(...)처럼 짧게 호출할 수 있다.
$style-rem-amount: 10 !default;

// rem: px 수치를 rem으로 환산한다.
// - module.ts styleRem과 동일하게 10px 기준 단위를 사용한다.
// - 단위가 이미 붙어 있는 값은 그대로 반환해 중복 환산을 방지한다.
@function rem($value) {
  @if type-of($value) != "number" {
    // number가 아니면 CSS-in-JS 버전과 동일하게 경고 후 원본을 돌려준다.
    @warn "rem: number만 전달할 수 있습니다.";
    @return $value;
  }

  @if not math.is-unitless($value) {
    // px 같은 단위가 이미 붙어 있으면 그대로 사용한다.
    @return $value;
  }

  // 10px 기준 rem 길이를 계산한다.
  @return math.div($value, $style-rem-amount) * 1rem;
}

// base-size: px/rem/string 값을 유효한 CSS 길이로 변환한다.
// - styleBaseSize의 unit/number/string/alt 분기와 동일하게 동작한다.
// - alt까지 유효 값이 없으면 auto를 반환하고, 허용 단위(px/rem)가 아니면 px로 강제한다.
@function base-size($unit, $value, $alt: null) {
  $unit: string.to-lower-case(string.unquote(#{$unit}));

  @if $unit != "px" and $unit != "rem" {
    // 허용되지 않은 단위가 들어오면 px 강제 후 동일 경고를 남긴다.
    @warn "base-size: #{$unit}는 px/rem만 허용되어 px로 대체합니다.";
    $unit: "px";
  }

  @if type-of($value) == "number" {
    // px 단위가 붙은 number면 그대로 통과한다.
    @if math.is-unitless($value) == false {
      @return $value;
    }

    // rem을 요청한 경우 rem 함수를 통해 환산한다.
    @if $unit == "rem" {
      @return rem($value);
    }

    // px 요청이면 px 단위를 붙인다.
    @return $value * 1px;
  }

  // 빈 문자열이 아닌 string이면 그대로 사용한다.
  @if type-of($value) == "string" and string.length($value) > 0 {
    @return $value;
  }

  // alt 값이 설정돼 있으면 동일 규칙으로 재귀 호출한다.
  @if $alt != null {
    @return base-size($unit, $alt);
  }

  // 값이 완전히 비어 있으면 auto를 기본값으로 사용한다.
  @if $value == null {
    @return auto;
  }

  // 마지막으로 남은 경우 그대로 문자열로 되돌린다.
  @return $value;
}

// spacing-array: padding/margin 리스트를 공백 문자열로 결합한다.
// - styleSpacingArray 문서의 파라미터 패턴[상하좌우/상하,좌우/... ]과 동일한 순서를 유지한다.
@function spacing-array($unit, $values) {
  @if type-of($values) != "list" {
    // 리스트가 아니면 단일 값으로 간주한다.
    @return base-size($unit, $values);
  }

  $result: ();

  @for $index from 1 through list.length($values) {
    $entry: list.nth($values, $index);
    // 각 항목을 base-size로 정규화한 뒤 공백으로 결합한다.
    $result: list.append($result, base-size($unit, $entry), "space");
  }

  @return $result;
}

// spacing-size: padding/margin 입력을 CSS 길이 값으로 정규화한다.
// - styleSpacingSize 분기 그대로: undefined→alt/0, number|string→base-size, array→spacing-array.
@function spacing-size($unit, $spacing: null, $alt: null) {
  @if $spacing == null {
    @if $alt == null {
      // spacing/alt 모두 없으면 0을 기본값으로 사용한다.
      @return 0;
    }

    @if type-of($alt) == "list" {
      // alt가 리스트인 경우 spacing-array 규칙을 그대로 적용한다.
      @return spacing-array($unit, $alt);
    }

    // alt가 number|string이면 base-size로 정규화한다.
    @return base-size($unit, $alt);
  }

  @if type-of($spacing) ==
    "number" or
    (type-of($spacing) == "string" and string.length($spacing) > 0)
  {
    // number/string 입력은 base-size에서 처리한다.
    @return base-size($unit, $spacing);
  }

  @if type-of($spacing) == "list" {
    // 리스트 입력은 spacing-array로 전달한다.
    @return spacing-array($unit, $spacing);
  }

  // 배열/문자열/숫자 외 타입은 문자열로 강제한다.
  @return $spacing;
}

// padding-size: spacing-size를 감싸 padding 맥락을 명확히 한다.
@function padding-size($unit, $padding: null, $alt: null) {
  @return spacing-size($unit, $padding, $alt);
}

// margin-size: spacing-size를 감싸 margin 맥락을 명확히 한다.
@function margin-size($unit, $margin: null, $alt: null) {
  @return spacing-size($unit, $margin, $alt);
}

// spacing: padding/margin 선언 반복을 줄이는 믹스인.
// - property 파라미터로 padding/margin 외 확장도 가능하다.
// - $important 인자로 !important 여부를 제어한다.
@mixin spacing($property, $unit, $value: null, $alt: null, $important: false) {
  $result: spacing-size($unit, $value, $alt);

  @if $important {
    // !important 플래그가 true인 경우.
    #{$property}: #{$result} !important;
  } @else {
    #{$property}: #{$result};
  }
}

// padding: spacing 믹스인을 padding 속성으로 고정한 래퍼.
@mixin padding($unit, $value: null, $alt: null, $important: false) {
  @include spacing(padding, $unit, $value, $alt, $important);
}

// margin: spacing 믹스인을 margin 속성으로 고정한 래퍼.
@mixin margin($unit, $value: null, $alt: null, $important: false) {
  @include spacing(margin, $unit, $value, $alt, $important);
}
