import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable, inject } from '@angular/core';
import { Observable } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { of } from 'rxjs';
import { PartnerCredential, RegisterCredentialRequest } from '../models/partner-credential.model';
import { CreateNodeRequest, Environment, NodeStatus, ProvisioningNode } from '../models/provisioning-node.model';
import { PartnerHierarchyGateway } from './partner-hierarchy-gateway.interface';

/**
 * Contract: docs/backend/PARTNER-PROVISIONING-API-CONTRACT.md §4, §6.
 *
 * Note this is the INBOUND partner API viewed from our own admin side - the
 * same endpoints partners call, used here so our backoffice sees exactly the
 * hierarchy a partner sees, rather than a parallel internal view that could
 * drift from it.
 */
@Injectable({ providedIn: 'root' })
export class PartnerHierarchyApiGateway implements PartnerHierarchyGateway {
  private readonly http = inject(HttpClient);

  loadHierarchy(companyId: string, environment: Environment): Observable<ProvisioningNode[]> {
    const params = new HttpParams().set('environment', environment);
    return this.http.get<ProvisioningNode[]>(
      `/api/partner/v1/companies/${encodeURIComponent(companyId)}/hierarchy`,
      { params },
    );
  }

  loadNode(nodeId: string): Observable<ProvisioningNode | null> {
    return this.http.get<ProvisioningNode | null>(`/api/partner/v1/nodes/${encodeURIComponent(nodeId)}`);
  }

  lookupByExternalReference(
    externalReference: string,
    environment: Environment,
  ): Observable<ProvisioningNode | null> {
    const params = new HttpParams()
      .set('externalReference', externalReference)
      .set('environment', environment);
    // §4.2: an unmatched reference is 404, never a partial match. Translated
    // to null so callers branch on a value rather than catching an error.
    return this.http
      .get<ProvisioningNode>('/api/partner/v1/nodes/lookup', { params })
      .pipe(catchError(() => of(null)));
  }

  createProject(companyId: string, request: CreateNodeRequest): Observable<ProvisioningNode> {
    return this.http.post<ProvisioningNode>(
      `/api/partner/v1/companies/${encodeURIComponent(companyId)}/projects`,
      request,
    );
  }

  createStore(projectId: string, request: CreateNodeRequest): Observable<ProvisioningNode> {
    return this.http.post<ProvisioningNode>(
      `/api/partner/v1/projects/${encodeURIComponent(projectId)}/stores`,
      request,
    );
  }

  createPaymentPoint(storeId: string, request: CreateNodeRequest): Observable<ProvisioningNode> {
    return this.http.post<ProvisioningNode>(
      `/api/partner/v1/stores/${encodeURIComponent(storeId)}/payment-points`,
      request,
    );
  }

  setStatus(
    nodeId: string,
    status: Extract<NodeStatus, 'active' | 'suspended'>,
    reason?: string,
  ): Observable<ProvisioningNode> {
    return this.http.patch<ProvisioningNode>(
      `/api/partner/v1/nodes/${encodeURIComponent(nodeId)}/status`,
      reason ? { status, reason } : { status },
    );
  }

  disable(nodeId: string): Observable<ProvisioningNode> {
    return this.http.post<ProvisioningNode>(
      `/api/partner/v1/nodes/${encodeURIComponent(nodeId)}/disable`,
      {},
    );
  }

  loadCredentials(companyId: string): Observable<PartnerCredential[]> {
    const params = new HttpParams().set('companyId', companyId);
    return this.http.get<PartnerCredential[]>('/api/partner/v1/credentials', { params });
  }

  registerCredential(request: RegisterCredentialRequest): Observable<PartnerCredential> {
    return this.http.post<PartnerCredential>('/api/partner/v1/credentials', request);
  }

  rotateCredential(keyId: string, newPublicKey: string): Observable<PartnerCredential> {
    return this.http.post<PartnerCredential>(
      `/api/partner/v1/credentials/${encodeURIComponent(keyId)}/rotate`,
      { publicKey: newPublicKey },
    );
  }

  revokeCredential(keyId: string): Observable<void> {
    return this.http
      .delete(`/api/partner/v1/credentials/${encodeURIComponent(keyId)}`)
      .pipe(map(() => undefined));
  }
}
