import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core';
import { catchError, of } from 'rxjs';
import { take } from 'rxjs/operators';
import { SOCIAL_IDENTITY_GATEWAY } from '../../../../core/identity/services/social-identity-gateway.token';
import { SocialProvider } from '../../../../core/identity/services/social-identity-gateway.interface';
import {
  ExternalIdentity,
  ExternalIdentityProvider,
} from '../../../../core/identity/models/customer-identity.model';
import { SocialLoginButtonComponent } from '../../../../components/social-login-button/social-login-button.component';
import { IconComponent } from '../../../../shared/ui/icon/icon.component';

/** Providers a customer can attach through an OAuth authorize redirect. */
const LINKABLE_SOCIAL: readonly SocialProvider[] = ['vk', 'yandex'];

const PROVIDER_LABEL: Record<ExternalIdentityProvider, string> = {
  vk_id: 'VK ID',
  yandex_id: 'Yandex ID',
  telegram: 'Telegram',
  max: 'MAX',
};

/** Which ExternalIdentity.provider a given OAuth SocialProvider produces. */
const SOCIAL_TO_IDENTITY: Record<SocialProvider, ExternalIdentityProvider> = {
  vk: 'vk_id',
  yandex: 'yandex_id',
};

type LoadState = 'loading' | 'ready' | 'error';

/**
 * FH-4.7. Account screen for managing linked external identities: shows what
 * is linked, lets the customer attach the OAuth providers they have not linked
 * yet, and detach any of them.
 *
 * Contract: docs/backend/PHASE-8-IDENTITY-MESSAGING-CONTRACT.md §2.1
 * (GET /me/identities, POST /{provider}/unlink, GET /{provider}/authorize).
 *
 * Two rules this UI has to honour, both enforced server-side but surfaced
 * here so the customer is never surprised:
 *   - the last remaining identity cannot be unlinked (it is the only way back
 *     in), so the detach control is disabled when exactly one is linked;
 *   - attempting to link a provider account already bound to a different
 *     customer is an identity conflict, not a silent rebind - the backend
 *     returns it as such and this screen shows the conflict rather than
 *     pretending the link succeeded.
 *
 * Not yet wired into a route: there is no customer account area in the
 * storefront yet, and no real OAuth application to authorize against
 * (FH-0.1). This is the surface those depend on, buildable and testable now.
 */
@Component({
  selector: 'app-account-identities',
  standalone: true,
  imports: [SocialLoginButtonComponent, IconComponent],
  templateUrl: './account-identities.component.html',
  styleUrls: ['./account-identities.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AccountIdentitiesComponent {
  private readonly gateway = inject(SOCIAL_IDENTITY_GATEWAY);

  readonly state = signal<LoadState>('loading');
  readonly identities = signal<ExternalIdentity[]>([]);
  /** Set when a link attempt hit an identity conflict (§2.3). */
  readonly conflict = signal<string | null>(null);
  /** Provider currently being detached, so only its button shows a spinner. */
  readonly unlinking = signal<ExternalIdentityProvider | null>(null);

  /** True while exactly one identity remains - detaching it is refused. */
  readonly isLastIdentity = computed(() => this.identities().length <= 1);

  /** OAuth providers not yet linked, offered as attach buttons. */
  readonly linkable = computed<SocialProvider[]>(() => {
    const linked = new Set(this.identities().map(identity => identity.provider));
    return LINKABLE_SOCIAL.filter(social => !linked.has(SOCIAL_TO_IDENTITY[social]));
  });

  constructor() {
    this.reload();
  }

  label(provider: ExternalIdentityProvider): string {
    return PROVIDER_LABEL[provider];
  }

  reload(): void {
    this.state.set('loading');
    this.gateway
      .listIdentities()
      .pipe(
        take(1),
        catchError(() => {
          this.state.set('error');
          return of<ExternalIdentity[] | null>(null);
        }),
      )
      .subscribe(identities => {
        if (identities === null) {
          return;
        }
        this.identities.set(identities);
        this.state.set('ready');
      });
  }

  unlink(provider: ExternalIdentityProvider): void {
    if (this.isLastIdentity() || this.unlinking()) {
      return;
    }
    this.conflict.set(null);
    this.unlinking.set(provider);
    this.gateway
      .unlink(provider)
      .pipe(
        take(1),
        catchError(() => {
          this.unlinking.set(null);
          this.state.set('error');
          return of<'failed'>('failed');
        }),
      )
      .subscribe(result => {
        if (result === 'failed') {
          return;
        }
        this.identities.update(list => list.filter(identity => identity.provider !== provider));
        this.unlinking.set(null);
      });
  }
}
