import { Component, Input, OnChanges, OnInit, forwardRef } from '@angular/core';
import {
  ControlValueAccessor,
  FormsModule,
  NG_VALUE_ACCESSOR,
} from '@angular/forms';
import { MatSelectModule } from '@angular/material/select';
import { MatButtonModule } from '@angular/material/button';

type Period = 'AM' | 'PM';

@Component({
  selector: 'cal-time-picker',
  standalone: true,
  imports: [FormsModule, MatSelectModule, MatButtonModule],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => TimePickerComponent),
      multi: true,
    },
  ],
  template: `
    <div class="tp" [class.is-disabled]="disabled">
      <div class="tp-box" [attr.aria-label]="label || 'Select time'">
        <div class="tp-clock">
          <mat-select
            class="tp-select tp-hour"
            [ngModel]="hour12"
            (ngModelChange)="onHourChange($event)"
            (blur)="onTouched()"
            [disabled]="disabled"
            panelClass="tp-panel"
            hideSingleSelectionIndicator
            aria-label="Hour"
          >
            @for (h of hours12; track h) {
              <mat-option [value]="h">{{ h }}</mat-option>
            }
          </mat-select>
          <span class="tp-colon" aria-hidden="true">:</span>
          <mat-select
            class="tp-select tp-minute"
            [ngModel]="minute"
            (ngModelChange)="onMinuteChange($event)"
            (blur)="onTouched()"
            [disabled]="disabled"
            panelClass="tp-panel"
            hideSingleSelectionIndicator
            aria-label="Minute"
          >
            @for (m of minuteOptions; track m) {
              <mat-option [value]="m">{{ formatMinute(m) }}</mat-option>
            }
          </mat-select>
        </div>

        <div class="tp-ampm" role="group" aria-label="AM or PM">
          <button
            type="button"
            class="tp-ampm-btn"
            [class.is-active]="period === 'AM'"
            [disabled]="disabled"
            (click)="onPeriodChange('AM')"
          >
            AM
          </button>
          <button
            type="button"
            class="tp-ampm-btn"
            [class.is-active]="period === 'PM'"
            [disabled]="disabled"
            (click)="onPeriodChange('PM')"
          >
            PM
          </button>
        </div>
      </div>
    </div>
  `,
  styles: [
    `
      .tp {
        display: block;
      }

      .tp-box {
        display: inline-flex;
        align-items: stretch;
        min-width: 11.5rem;
        background: #fff;
        border: 1px solid var(--cal-border, #e2e8f0);
        border-radius: 10px;
        overflow: hidden;
        transition: border-color 0.15s, box-shadow 0.15s;
      }

      .tp-box:focus-within {
        border-color: var(--cal-brand, #4f46e5);
        box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.14);
      }

      .tp.is-disabled .tp-box {
        background: #f8fafc;
        opacity: 0.75;
      }

      .tp-clock {
        display: flex;
        align-items: center;
        padding: 0.35rem 0.5rem 0.35rem 0.65rem;
        gap: 0.1rem;
        flex: 1;
        min-width: 0;
      }

      .tp-colon {
        font-size: 1rem;
        font-weight: 700;
        color: var(--cal-text-muted, #64748b);
        line-height: 1;
        padding: 0 0.1rem;
        user-select: none;
      }

      .tp-select {
        flex-shrink: 0;
      }

      .tp-select ::ng-deep .mat-mdc-select-trigger {
        padding: 0 0.25rem;
        min-height: 32px;
        gap: 0.15rem;
      }

      .tp-select ::ng-deep .mat-mdc-select-value {
        max-width: none !important;
        overflow: visible !important;
      }

      .tp-select ::ng-deep .mat-mdc-select-value-text {
        overflow: visible !important;
        text-overflow: clip !important;
        font-size: 0.95rem;
        font-weight: 700;
        font-variant-numeric: tabular-nums;
        color: var(--cal-text, #0f172a);
      }

      .tp-select ::ng-deep .mat-mdc-select-arrow-wrapper {
        transform: none;
        padding-left: 0;
      }

      .tp-select ::ng-deep .mat-mdc-select-arrow {
        width: 10px;
        height: 10px;
        color: var(--cal-text-muted, #64748b);
      }

      .tp-hour {
        width: 2.75rem;
      }

      .tp-minute {
        width: 3.1rem;
      }

      .tp-ampm {
        display: flex;
        flex-direction: column;
        border-left: 1px solid var(--cal-border, #e2e8f0);
        flex-shrink: 0;
      }

      .tp-ampm-btn {
        flex: 1;
        min-width: 2.75rem;
        padding: 0 0.5rem;
        border: none;
        background: #f8fafc;
        font-size: 0.68rem;
        font-weight: 700;
        letter-spacing: 0.03em;
        color: var(--cal-text-muted, #64748b);
        cursor: pointer;
        transition: background 0.12s, color 0.12s;
      }

      .tp-ampm-btn:first-child {
        border-bottom: 1px solid var(--cal-border, #e2e8f0);
      }

      .tp-ampm-btn.is-active {
        background: var(--cal-brand-soft, #eef2ff);
        color: var(--cal-brand, #4f46e5);
      }

      .tp-ampm-btn:hover:not(:disabled):not(.is-active) {
        background: #f1f5f9;
      }

      .tp-ampm-btn:disabled {
        cursor: not-allowed;
      }
    `,
  ],
})
export class TimePickerComponent implements ControlValueAccessor, OnChanges, OnInit {
  @Input() label = '';
  @Input() minuteStep = 15;

  hours12 = Array.from({ length: 12 }, (_, i) => i + 1);
  minuteOptions: number[] = [];

  hour12 = 9;
  minute = 0;
  period: Period = 'AM';
  disabled = false;

  onChange: (value: string) => void = () => {};
  onTouched: () => void = () => {};

  ngOnInit(): void {
    this.rebuildMinuteOptions();
  }

  ngOnChanges(): void {
    this.rebuildMinuteOptions();
  }

  writeValue(value: string | null): void {
    this.rebuildMinuteOptions();
    const parsed = this.parse24h(value ?? '09:00');
    this.hour12 = parsed.hour12;
    this.minute = parsed.minute;
    this.period = parsed.period;
  }

  registerOnChange(fn: (value: string) => void): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: () => void): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  onHourChange(hour: number): void {
    this.hour12 = hour;
    this.emit();
  }

  onMinuteChange(minute: number): void {
    this.minute = minute;
    this.emit();
  }

  onPeriodChange(period: Period): void {
    if (this.period === period) return;
    this.period = period;
    this.onTouched();
    this.emit();
  }

  formatMinute(m: number): string {
    return m.toString().padStart(2, '0');
  }

  private emit(): void {
    this.onChange(this.to24h(this.hour12, this.minute, this.period));
  }

  private rebuildMinuteOptions(): void {
    const step = Math.max(1, Math.min(60, this.minuteStep));
    const options: number[] = [];
    for (let m = 0; m < 60; m += step) {
      options.push(m);
    }
    this.minuteOptions = options;
  }

  private parse24h(value: string): { hour12: number; minute: number; period: Period } {
    const match = /^(\d{1,2}):(\d{2})$/.exec(value.trim());
    let h24 = match ? Number(match[1]) : 9;
    let rawMinute = match ? Number(match[2]) : 0;

    h24 = Math.max(0, Math.min(23, h24));
    rawMinute = this.snapMinute(rawMinute);

    const period: Period = h24 >= 12 ? 'PM' : 'AM';
    const hour12 = h24 % 12 || 12;

    return { hour12, minute: rawMinute, period };
  }

  private snapMinute(minute: number): number {
    const step = Math.max(1, Math.min(60, this.minuteStep));
    const snapped = Math.round(minute / step) * step;
    return snapped >= 60 ? 60 - step : snapped;
  }

  private to24h(hour12: number, minute: number, period: Period): string {
    let h24 = hour12 % 12;
    if (period === 'PM') h24 += 12;
    return `${h24.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
  }
}
