import { CommonModule } from '@angular/common';
import { Component, OnDestroy, computed, inject, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { InvimaDataset, InvimaSyncJob, InvimaSyncStats } from '../../core/models';
import { InvimaService } from '../../core/services/invima.service';

@Component({
  selector: 'app-sync-page',
  standalone: true,
  imports: [CommonModule, FormsModule],
  template: `
    <section class="sync-page">
      <header class="head card">
        <p class="eyebrow">Modulo tecnico</p>
        <h2>Sincronizacion INVIMA</h2>
        <p>Actualiza datasets y reconstruye la base normalizada para consultas de alto rendimiento.</p>
      </header>

      <section class="stats card">
        <header class="stats-head">
          <div>
            <h3>Estado actual de registros</h3>
            <small>Actualizacion automatica cada 10 segundos</small>
          </div>
          <span class="stamp">{{ formatDateTime(stats()?.at) }}</span>
        </header>

        <div class="kpis">
          <article>
            <small>Total local</small>
            <strong>{{ formatNumber(stats()?.totals?.catalogRows) }}</strong>
          </article>
          <article>
            <small>Vigentes</small>
            <strong>{{ formatNumber(stats()?.totals?.vigentes) }}</strong>
          </article>
          <article>
            <small>Vencidos</small>
            <strong>{{ formatNumber(stats()?.totals?.vencidos) }}</strong>
          </article>
          <article>
            <small>Categorias</small>
            <strong>{{ formatNumber(stats()?.totals?.categories) }}</strong>
          </article>
          <article class="accent" *ngIf="loading()">
            <small>Avance en esta corrida</small>
            <strong>+{{ formatNumber(liveAdded()) }}</strong>
          </article>
        </div>

        <p class="stats-error" *ngIf="statsError()">{{ statsError() }}</p>
        <p class="stats-error" *ngIf="jobError()">{{ jobError() }}</p>

        <div class="dataset-live" *ngIf="stats() as snapshot">
          <div class="dataset-row" *ngFor="let item of snapshot.datasets">
            <div class="dataset-name">
              <strong>{{ item.name }}</strong>
              <small>{{ item.key }}</small>
            </div>
            <div class="dataset-values">
              <span>{{ formatNumber(item.localRows) }} registros</span>
              <span class="delta" *ngIf="loading()">+{{ formatNumber(datasetDelta(item.key, item.localRows)) }}</span>
            </div>
          </div>
        </div>
      </section>

      <section class="panel card">
        <div class="controls">
          <label>
            Tamano de pagina
            <input type="number" min="100" max="5000" step="100" [(ngModel)]="pageSize" />
          </label>

          <label>
            Concurrencia
            <input type="number" min="1" max="8" [(ngModel)]="concurrency" />
          </label>
        </div>

        <div class="toolbar">
          <button type="button" class="ghost" (click)="selectAll(true)" [disabled]="loading()">Seleccionar todo</button>
          <button type="button" class="ghost" (click)="selectAll(false)" [disabled]="loading()">Limpiar seleccion</button>
          <button type="button" (click)="sync()" [disabled]="loading()">
            {{ loading() ? 'Sincronizando...' : 'Iniciar sincronizacion' }}
          </button>
        </div>

        <div class="dataset-grid">
          <label *ngFor="let item of datasets()" class="dataset-item">
            <input type="checkbox" [checked]="isSelected(item)" (change)="toggle(item, $any($event.target).checked)" />
            <div>
              <strong>{{ item.name }}</strong>
              <small>{{ item.key }} · {{ item.id }}</small>
            </div>
          </label>
        </div>

        <p class="message" *ngIf="message()">{{ message() }}</p>
      </section>
    </section>
  `,
  styles: `
    .sync-page {
      display: grid;
      gap: 14px;
    }

    .card {
      border-radius: 20px;
      border: 1px solid var(--color-border);
      background: var(--color-card);
      box-shadow: var(--shadow-soft);
      backdrop-filter: blur(3px);
    }

    .head {
      padding: 16px 18px;
    }

    .eyebrow {
      margin: 0;
      color: var(--color-primary);
      font-family: var(--font-display);
      font-weight: 700;
      letter-spacing: 0.03em;
      font-size: 0.78rem;
    }

    h2 {
      margin: 8px 0 0;
      font-family: var(--font-display);
      color: var(--color-ink);
      font-size: 1.5rem;
    }

    .head p {
      margin: 6px 0 0;
      color: var(--color-muted);
      font-size: 0.93rem;
    }

    .stats {
      padding: 14px;
      display: grid;
      gap: 10px;
    }

    .stats-head {
      display: flex;
      justify-content: space-between;
      align-items: flex-start;
      gap: 10px;
      flex-wrap: wrap;
    }

    .stats-head h3 {
      margin: 0;
      font-family: var(--font-display);
      color: var(--color-ink);
      font-size: 1.05rem;
    }

    .stats-head small {
      color: #5c6f88;
      font-size: 0.8rem;
    }

    .stamp {
      color: #40566f;
      font-size: 0.8rem;
      font-weight: 600;
      border: 1px solid rgba(160, 168, 179, 0.46);
      background: rgba(255, 255, 255, 0.66);
      border-radius: 999px;
      padding: 5px 9px;
    }

    .kpis {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
      gap: 8px;
    }

    .kpis article {
      border: 1px solid rgba(160, 168, 179, 0.52);
      border-radius: 12px;
      background: rgba(255, 255, 255, 0.74);
      padding: 10px;
      display: grid;
      gap: 4px;
    }

    .kpis small {
      color: #5c7088;
      font-size: 0.75rem;
      font-weight: 700;
      text-transform: uppercase;
      letter-spacing: 0.03em;
    }

    .kpis strong {
      color: #1d3448;
      font-family: var(--font-display);
      font-size: 1.1rem;
      line-height: 1.1;
    }

    .kpis .accent {
      border-color: rgba(32, 178, 170, 0.42);
      background: rgba(32, 178, 170, 0.14);
    }

    .kpis .accent strong {
      color: #0f7470;
    }

    .stats-error {
      margin: 0;
      color: #b71717;
      font-size: 0.86rem;
      font-weight: 600;
    }

    .dataset-live {
      display: grid;
      gap: 7px;
    }

    .dataset-row {
      border: 1px solid rgba(160, 168, 179, 0.45);
      border-radius: 11px;
      background: rgba(255, 255, 255, 0.64);
      padding: 8px 10px;
      display: flex;
      justify-content: space-between;
      gap: 10px;
      align-items: center;
      flex-wrap: wrap;
    }

    .dataset-name {
      display: grid;
      gap: 1px;
    }

    .dataset-name strong {
      color: #1f3448;
      font-size: 0.88rem;
      line-height: 1.25;
    }

    .dataset-name small {
      color: #61758f;
      font-size: 0.76rem;
    }

    .dataset-values {
      display: inline-flex;
      align-items: center;
      gap: 10px;
      color: #2f4c64;
      font-size: 0.84rem;
      font-weight: 700;
    }

    .delta {
      color: #0f7470;
    }

    .panel {
      padding: 14px;
      display: grid;
      gap: 12px;
    }

    .controls {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
      gap: 10px;
    }

    label {
      display: grid;
      gap: 6px;
      color: #344b62;
      font-size: 0.85rem;
      font-weight: 700;
    }

    input[type='number'] {
      border: 1px solid rgba(139, 148, 162, 0.52);
      border-radius: 12px;
      padding: 9px 11px;
      font-size: 0.9rem;
      background: rgba(255, 255, 255, 0.88);
      outline: none;
    }

    input[type='number']:focus-visible {
      border-color: rgba(32, 178, 170, 0.5);
      box-shadow: 0 0 0 3px rgba(32, 178, 170, 0.12);
    }

    .toolbar {
      display: flex;
      flex-wrap: wrap;
      gap: 8px;
    }

    button {
      border-radius: 12px;
      border: 0;
      padding: 9px 13px;
      font-size: 0.9rem;
      font-weight: 700;
      cursor: pointer;
      background: linear-gradient(90deg, var(--color-primary), var(--color-primary-strong));
      color: #fff;
      box-shadow: 0 10px 20px rgba(32, 178, 170, 0.24);
    }

    button.ghost {
      background: #fff;
      border: 1px solid rgba(32, 178, 170, 0.26);
      color: var(--color-primary-strong);
      box-shadow: none;
    }

    button:disabled {
      opacity: 0.72;
      cursor: not-allowed;
    }

    .dataset-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
      gap: 8px;
    }

    .dataset-item {
      border: 1px solid rgba(160, 168, 179, 0.52);
      border-radius: 12px;
      background: rgba(255, 255, 255, 0.72);
      padding: 10px;
      display: grid;
      grid-template-columns: auto 1fr;
      gap: 8px;
      align-items: center;
    }

    .dataset-item strong {
      color: #1f3448;
      font-size: 0.87rem;
      line-height: 1.3;
    }

    .dataset-item small {
      color: #647790;
      font-size: 0.76rem;
    }

    .message {
      margin: 0;
      color: #37556d;
      font-weight: 600;
      font-size: 0.9rem;
    }
  `
})
export class SyncPageComponent implements OnDestroy {
  private readonly invima = inject(InvimaService);
  private readonly refreshMs = 10_000;
  private refreshHandle: ReturnType<typeof setInterval> | null = null;
  private announcedFinalJobId: string | null = null;

  readonly loading = signal(false);
  readonly message = signal('');
  readonly statsError = signal('');
  readonly jobError = signal('');
  readonly datasets = signal<InvimaDataset[]>([]);
  readonly selected = signal<string[]>([]);
  readonly stats = signal<InvimaSyncStats | null>(null);
  readonly job = signal<InvimaSyncJob | null>(null);
  readonly baselineTotal = signal<number | null>(null);
  readonly baselineByDataset = signal<Record<string, number>>({});

  readonly liveAdded = computed(() => {
    const current = Number(this.stats()?.totals?.catalogRows || 0);
    const base = this.baselineTotal();
    if (base === null) return 0;
    return Math.max(0, current - base);
  });

  pageSize = 1000;
  concurrency = 4;

  constructor() {
    this.invima.getDatasets().subscribe({
      next: (response) => this.datasets.set(response.items || []),
      error: () => this.datasets.set([])
    });

    this.loadStats();
    this.loadJob();
    this.refreshHandle = window.setInterval(() => {
      this.loadStats();
      this.loadJob();
    }, this.refreshMs);
  }

  ngOnDestroy() {
    if (this.refreshHandle) {
      clearInterval(this.refreshHandle);
      this.refreshHandle = null;
    }
  }

  private loadStats() {
    this.invima.getSyncStats().subscribe({
      next: (response) => {
        this.stats.set(response);
        this.statsError.set('');

        if (this.loading()) {
          this.ensureBaseline(response);
          this.refreshLiveMessage(response);
        }
      },
      error: () => {
        this.statsError.set('No se pudo actualizar el progreso en vivo.');
      }
    });
  }

  private loadJob() {
    this.invima.getSyncJob().subscribe({
      next: (response) => {
        const job = response.job || null;
        this.job.set(job);
        this.jobError.set('');

        if (job?.status === 'running') {
          this.loading.set(true);
          this.announcedFinalJobId = null;
          if (this.baselineTotal() === null && this.stats()) {
            this.captureBaselineFromCurrentStats();
          }
          this.refreshLiveMessage(this.stats());
          return;
        }

        if (!job && this.loading()) {
          this.loading.set(false);
          this.message.set(
            'La sincronizacion se interrumpio o el backend se reinicio. Los datos ya guardados se conservan; puedes iniciar de nuevo para retomar.'
          );
          this.loadStats();
          return;
        }

        if (this.loading()) {
          this.loading.set(false);
        }

        if (
          (job?.status === 'completed' || job?.status === 'failed') &&
          this.announcedFinalJobId !== job.id
        ) {
          this.announcedFinalJobId = job.id;
          this.message.set(this.buildFinalMessage(job));
          this.loadStats();
        }
      },
      error: () => {
        this.jobError.set('No se pudo consultar el estado de la sincronizacion.');
      }
    });
  }

  private ensureBaseline(snapshot: InvimaSyncStats) {
    if (this.baselineTotal() === null) {
      this.baselineTotal.set(Number(snapshot.totals?.catalogRows || 0));
    }

    const currentMap = { ...this.baselineByDataset() };
    let changed = false;
    for (const item of snapshot.datasets || []) {
      if (currentMap[item.key] === undefined) {
        currentMap[item.key] = Number(item.localRows || 0);
        changed = true;
      }
    }

    if (changed) {
      this.baselineByDataset.set(currentMap);
    }
  }

  private refreshLiveMessage(snapshot: InvimaSyncStats | null) {
    const job = this.job();
    if (job?.status === 'running') {
      const total = this.formatNumber(snapshot?.totals?.catalogRows);
      const added = this.formatNumber(this.liveAdded());
      this.message.set(
        `Sincronizando... datasets ${this.formatNumber(job.completed)}/${this.formatNumber(job.requested)}. ` +
          `Procesadas ${this.formatNumber(job.fetched)}, guardadas/actualizadas ${this.formatNumber(
            job.persisted
          )}, paginas ${this.formatNumber(job.pages)}, fallos ${this.formatNumber(job.failed)}. ` +
          `Avance local +${added} registros (total local ${total}).`
      );
      return;
    }

    if (!snapshot) return;

    const selectedKeys = this.selected().length
      ? this.selected()
      : this.datasets().map((item) => item.key);

    const detail = (snapshot.datasets || [])
      .filter((item) => selectedKeys.includes(item.key))
      .map((item) => `${item.key}: +${this.formatNumber(this.datasetDelta(item.key, item.localRows))}`)
      .join(' | ');

    const total = this.formatNumber(snapshot.totals?.catalogRows);
    const added = this.formatNumber(this.liveAdded());
    this.message.set(
      `Sincronizando... avance +${added} registros (total local ${total}).${detail ? ` ${detail}` : ''}`
    );
  }

  private buildFinalMessage(job: InvimaSyncJob) {
    const statusText =
      job.status === 'completed' ? 'Sincronizacion finalizada.' : 'Sincronizacion finalizada con errores.';
    const failedKeys = job.datasets
      .filter((item) => item.status === 'failed')
      .map((item) => item.key)
      .join(', ');
    const failures = job.failed ? ` Fallaron ${this.formatNumber(job.failed)} dataset(s): ${failedKeys}.` : '';

    return (
      `${statusText} Datasets exitosos: ${this.formatNumber(job.completed)}/${this.formatNumber(job.requested)}. ` +
      `Filas procesadas: ${this.formatNumber(job.fetched)}. Filas guardadas/actualizadas: ${this.formatNumber(
        job.persisted
      )}.${failures}`
    );
  }

  private captureBaselineFromCurrentStats() {
    const snapshot = this.stats();
    if (!snapshot) {
      this.baselineTotal.set(null);
      this.baselineByDataset.set({});
      return;
    }

    this.baselineTotal.set(Number(snapshot.totals?.catalogRows || 0));
    const map: Record<string, number> = {};
    for (const item of snapshot.datasets || []) {
      map[item.key] = Number(item.localRows || 0);
    }
    this.baselineByDataset.set(map);
  }

  datasetDelta(key: string, current: number) {
    const base = this.baselineByDataset()[key];
    if (base === undefined) return 0;
    return Math.max(0, Number(current || 0) - Number(base || 0));
  }

  formatNumber(value: number | null | undefined) {
    const numeric = Number(value || 0);
    return new Intl.NumberFormat('es-CO').format(numeric);
  }

  formatDateTime(value: string | null | undefined) {
    if (!value) return 'Sin actualizacion';
    const date = new Date(value);
    if (Number.isNaN(date.getTime())) return value;
    return date.toLocaleString('es-CO', { hour12: true });
  }

  toggle(item: InvimaDataset, checked: boolean) {
    const current = this.selected();
    if (checked) {
      if (current.includes(item.key)) return;
      this.selected.set([...current, item.key]);
      return;
    }
    this.selected.set(current.filter((key) => key !== item.key));
  }

  isSelected(item: InvimaDataset) {
    return this.selected().includes(item.key);
  }

  selectAll(value: boolean) {
    if (!value) {
      this.selected.set([]);
      return;
    }
    this.selected.set(this.datasets().map((item) => item.key));
  }

  sync() {
    this.loading.set(true);
    this.message.set('Iniciando sincronizacion de datasets...');
    this.captureBaselineFromCurrentStats();
    this.loadStats();

    this.invima
      .sync(this.selected(), Math.max(100, Number(this.pageSize) || 1000), Math.max(1, Number(this.concurrency) || 4))
      .subscribe({
        next: (response) => {
          this.job.set(response.job || null);
          if (response.alreadyRunning) {
            this.message.set('Ya hay una sincronizacion en curso. Retomando progreso...');
          }
          this.refreshLiveMessage(this.stats());
          this.loadJob();
          this.loadStats();
        },
        error: () => {
          this.loading.set(false);
          this.message.set('No fue posible sincronizar en este momento.');
        }
      });
  }
}
