import { test, expect } from '@playwright/test';
import { getCachedTokens, authHeaders } from './helpers';

const API_URL = process.env.E2E_API_URL || 'http://localhost:3000';

/**
 * Сквозное тестирование MVP-потока FitCRM.
 *
 * Все тесты запускаются последовательно (serial) и используют
 * кешированные токены из global-setup (без rate limit проблем).
 */

// Shared auth state — from cached tokens
const coordTokens = getCachedTokens('coordinator');
const trainerTokens = getCachedTokens('trainer');
const clubAdminTokens = getCachedTokens('clubAdmin');
let trainerId: string;

test.describe.configure({ mode: 'serial' });

test.describe('MVP — Авторизация и подготовка', () => {
  test('login as coordinator', async () => {
    expect(coordTokens.accessToken).toBeTruthy();
  });

  test('login as trainer', async ({ request }) => {
    expect(trainerTokens.accessToken).toBeTruthy();

    const profileRes = await request.get(`${API_URL}/v1/auth/profile`, {
      headers: authHeaders(trainerTokens),
    });
    const profile = await profileRes.json();
    trainerId = profile.id;
    expect(trainerId).toBeTruthy();
  });

  test('login as club admin', async () => {
    expect(clubAdminTokens.accessToken).toBeTruthy();
  });
});

// -----------------------------------------------------------------------
// MVP End-to-End Flow: Client → Funnel → Training → Sale → Stats
// -----------------------------------------------------------------------
let createdClientId: string;
let createdTrainingId: string;
let createdSaleId: string;

test.describe('MVP — Сквозной поток', () => {
  test('1. Создание клиента (координатор)', async ({ request }) => {
    const uniquePhone = `+7800${Date.now().toString().slice(-7)}`;
    const response = await request.post(`${API_URL}/v1/clients`, {
      headers: authHeaders(coordTokens),
      data: {
        firstName: 'МВП',
        lastName: 'Тестклиент',
        phone: uniquePhone,
      },
    });

    expect(response.ok()).toBeTruthy();
    const client = await response.json();
    expect(client.id).toBeTruthy();
    expect(client.firstName).toBe('МВП');
    createdClientId = client.id;
  });

  test('2. Назначение тренера (воронка assign)', async ({ request }) => {
    const response = await request.post(`${API_URL}/v1/funnel/assign`, {
      headers: authHeaders(coordTokens),
      data: {
        clientId: createdClientId,
        trainerId: trainerId,
        comment: 'E2E: назначение тренера',
      },
    });

    expect(response.ok()).toBeTruthy();
    const entry = await response.json();
    expect(entry.stage).toBe('ASSIGNED');
    expect(entry.trainerId).toBe(trainerId);
  });

  test('3. Перевод на этап APPOINTMENT_SET', async ({ request }) => {
    const response = await request.post(`${API_URL}/v1/funnel/${createdClientId}/move`, {
      headers: authHeaders(trainerTokens),
      data: {
        stage: 'APPOINTMENT_SET',
        comment: 'Назначена стартовая тренировка',
      },
    });

    expect(response.ok()).toBeTruthy();
    const entry = await response.json();
    expect(entry.stage).toBe('APPOINTMENT_SET');
  });

  test('4. Создание тренировки', async ({ request }) => {
    const scheduledAt = new Date();
    scheduledAt.setDate(scheduledAt.getDate() + 7);
    scheduledAt.setHours(14, 0, 0, 0);

    const response = await request.post(`${API_URL}/v1/trainings`, {
      headers: authHeaders(trainerTokens),
      data: {
        clientId: createdClientId,
        scheduledAt: scheduledAt.toISOString(),
        duration: 60,
      },
    });

    expect(response.ok()).toBeTruthy();
    const training = await response.json();
    expect(training.id).toBeTruthy();
    expect(training.status).toBe('PLANNED');
    createdTrainingId = training.id;
  });

  test('5. Получение тренировки по ID', async ({ request }) => {
    const response = await request.get(`${API_URL}/v1/trainings/${createdTrainingId}`, {
      headers: authHeaders(trainerTokens),
    });

    expect(response.ok()).toBeTruthy();
    const training = await response.json();
    expect(training.id).toBe(createdTrainingId);
    expect(training.status).toBe('PLANNED');
  });

  test('6. Подтверждение тренировки', async ({ request }) => {
    const response = await request.post(`${API_URL}/v1/trainings/${createdTrainingId}/confirm`, {
      headers: authHeaders(trainerTokens),
    });

    expect(response.ok()).toBeTruthy();
    const training = await response.json();
    expect(training.status).toBe('CONFIRMED');
  });

  test('7. Завершение тренировки', async ({ request }) => {
    const response = await request.post(`${API_URL}/v1/trainings/${createdTrainingId}/complete`, {
      headers: authHeaders(trainerTokens),
    });

    expect(response.ok()).toBeTruthy();
    const training = await response.json();
    expect(training.status).toBe('COMPLETED');
  });

  test('8. Перевод клиента на этап COMPLETED', async ({ request }) => {
    const response = await request.post(`${API_URL}/v1/funnel/${createdClientId}/move`, {
      headers: authHeaders(trainerTokens),
      data: {
        stage: 'COMPLETED',
        comment: 'Пробная тренировка проведена',
      },
    });

    expect(response.ok()).toBeTruthy();
    const entry = await response.json();
    expect(entry.stage).toBe('COMPLETED');
  });

  test('9. Выставление продажи', async ({ request }) => {
    const response = await request.post(`${API_URL}/v1/sales`, {
      headers: authHeaders(trainerTokens),
      data: {
        clientId: createdClientId,
        amount: 5000,
        description: 'E2E: пакет тренировок',
      },
    });

    expect(response.ok()).toBeTruthy();
    const sale = await response.json();
    expect(sale.id).toBeTruthy();
    expect(sale.status).toBe('PENDING');
    expect(Number(sale.amount)).toBe(5000);
    createdSaleId = sale.id;
  });

  test('10. Долг отображается в списке', async ({ request }) => {
    const response = await request.get(`${API_URL}/v1/sales/debts`, {
      headers: authHeaders(trainerTokens),
    });

    expect(response.ok()).toBeTruthy();
    const body = await response.json();
    expect(body.data).toBeInstanceOf(Array);
    const ourDebt = body.data.find((s: Record<string, unknown>) => s.id === createdSaleId);
    expect(ourDebt).toBeTruthy();
    expect(ourDebt.status).toBe('PENDING');
  });

  test('11. Оплата продажи (админ клуба)', async ({ request }) => {
    const response = await request.post(`${API_URL}/v1/sales/${createdSaleId}/pay`, {
      headers: authHeaders(clubAdminTokens),
    });

    expect(response.ok()).toBeTruthy();
    const sale = await response.json();
    expect(sale.status).toBe('PAID');
  });

  test('12. Перевод клиента в REGULAR', async ({ request }) => {
    const response = await request.post(`${API_URL}/v1/funnel/${createdClientId}/move`, {
      headers: authHeaders(trainerTokens),
      data: {
        stage: 'REGULAR',
        comment: 'Купил пакет, постоянный клиент',
      },
    });

    expect(response.ok()).toBeTruthy();
    const entry = await response.json();
    expect(entry.stage).toBe('REGULAR');
  });

  test('13. Проверка истории воронки', async ({ request }) => {
    const response = await request.get(`${API_URL}/v1/funnel/client/${createdClientId}`, {
      headers: authHeaders(coordTokens),
    });

    expect(response.ok()).toBeTruthy();
    const result = await response.json();
    // API may return array directly or paginated {data: [...]}
    const entries = Array.isArray(result) ? result : result.data;
    expect(entries).toBeInstanceOf(Array);
    expect(entries.length).toBeGreaterThanOrEqual(1);
    // Latest entry should be REGULAR (most recent stage)
    expect(entries[0].stage).toBe('REGULAR');
  });

  test('14. Сводная статистика', async ({ request }) => {
    const response = await request.get(`${API_URL}/v1/stats/summary`, {
      headers: authHeaders(trainerTokens),
    });

    expect(response.ok()).toBeTruthy();
    const stats = await response.json();
    expect(stats).toHaveProperty('totalClients');
    expect(stats).toHaveProperty('totalTrainings');
    expect(stats).toHaveProperty('totalSalesAmount');
    expect(stats.totalClients).toBeGreaterThan(0);
  });

  test('15. Конверсия воронки', async ({ request }) => {
    const response = await request.get(`${API_URL}/v1/stats/funnel`, {
      headers: authHeaders(trainerTokens),
    });

    expect(response.ok()).toBeTruthy();
    const stats = await response.json();
    expect(stats).toHaveProperty('newToAssigned');
    expect(stats).toHaveProperty('assignedToCompleted');
    expect(stats).toHaveProperty('completedToRegular');
  });
});

// -----------------------------------------------------------------------
// Schedule Mutations
// -----------------------------------------------------------------------
test.describe('MVP — Расписание (мутации)', () => {
  let clientId: string;

  test('получить клиента для тестов', async ({ request }) => {
    const res = await request.get(`${API_URL}/v1/clients?limit=1`, {
      headers: authHeaders(trainerTokens),
    });
    const body = await res.json();
    clientId = body.data[0].id;
    expect(clientId).toBeTruthy();
  });

  test('создание + отмена тренировки', async ({ request }) => {
    const scheduledAt = new Date();
    scheduledAt.setDate(scheduledAt.getDate() + 14);
    scheduledAt.setHours(11, 0, 0, 0);

    const createRes = await request.post(`${API_URL}/v1/trainings`, {
      headers: authHeaders(trainerTokens),
      data: { clientId, scheduledAt: scheduledAt.toISOString(), duration: 60 },
    });
    expect(createRes.ok()).toBeTruthy();
    const training = await createRes.json();
    expect(training.status).toBe('PLANNED');

    const cancelRes = await request.post(`${API_URL}/v1/trainings/${training.id}/cancel`, {
      headers: authHeaders(trainerTokens),
      data: { cancelReason: 'CLIENT_CANCEL', cancelComment: 'E2E: отмена' },
    });
    expect(cancelRes.ok()).toBeTruthy();
    const cancelled = await cancelRes.json();
    expect(cancelled.status).toBe('CANCELLED');
  });

  test('создание + перенос тренировки', async ({ request }) => {
    const scheduledAt = new Date();
    scheduledAt.setDate(scheduledAt.getDate() + 15);
    scheduledAt.setHours(10, 0, 0, 0);

    const createRes = await request.post(`${API_URL}/v1/trainings`, {
      headers: authHeaders(trainerTokens),
      data: { clientId, scheduledAt: scheduledAt.toISOString(), duration: 60 },
    });
    expect(createRes.ok()).toBeTruthy();
    const training = await createRes.json();

    const newDate = new Date(scheduledAt);
    newDate.setDate(newDate.getDate() + 1);
    newDate.setHours(16, 0, 0, 0);

    const rescheduleRes = await request.post(`${API_URL}/v1/trainings/${training.id}/reschedule`, {
      headers: authHeaders(trainerTokens),
      data: { scheduledAt: newDate.toISOString(), duration: 90 },
    });
    expect(rescheduleRes.ok()).toBeTruthy();
    const rescheduled = await rescheduleRes.json();
    expect(rescheduled.duration).toBe(90);
  });

  test('список тренировок по дате', async ({ request }) => {
    const tomorrow = new Date();
    tomorrow.setDate(tomorrow.getDate() + 1);
    const dateStr = tomorrow.toISOString().split('T')[0];

    const response = await request.get(`${API_URL}/v1/trainings/date/${dateStr}`, {
      headers: authHeaders(trainerTokens),
    });

    expect(response.ok()).toBeTruthy();
    const trainings = await response.json();
    expect(trainings).toBeInstanceOf(Array);
  });

  test('список тренировок с пагинацией', async ({ request }) => {
    const response = await request.get(`${API_URL}/v1/trainings?limit=5`, {
      headers: authHeaders(trainerTokens),
    });

    expect(response.ok()).toBeTruthy();
    const body = await response.json();
    expect(body.data).toBeInstanceOf(Array);
    expect(body.data.length).toBeLessThanOrEqual(5);
    expect(body).toHaveProperty('hasMore');
  });
});

// -----------------------------------------------------------------------
// Sales Lifecycle
// -----------------------------------------------------------------------
test.describe('MVP — Продажи (жизненный цикл)', () => {
  let clientId: string;

  test('получить клиента для тестов', async ({ request }) => {
    const res = await request.get(`${API_URL}/v1/clients?limit=1`, {
      headers: authHeaders(trainerTokens),
    });
    clientId = (await res.json()).data[0].id;
    expect(clientId).toBeTruthy();
  });

  test('создание + оплата + проверка', async ({ request }) => {
    const createRes = await request.post(`${API_URL}/v1/sales`, {
      headers: authHeaders(trainerTokens),
      data: { clientId, amount: 3000, description: 'E2E: разовая' },
    });
    expect(createRes.ok()).toBeTruthy();
    const sale = await createRes.json();
    expect(sale.status).toBe('PENDING');

    const payRes = await request.post(`${API_URL}/v1/sales/${sale.id}/pay`, {
      headers: authHeaders(clubAdminTokens),
    });
    expect(payRes.ok()).toBeTruthy();
    expect((await payRes.json()).status).toBe('PAID');

    const getRes = await request.get(`${API_URL}/v1/sales/${sale.id}`, {
      headers: authHeaders(trainerTokens),
    });
    expect(getRes.ok()).toBeTruthy();
    expect((await getRes.json()).status).toBe('PAID');
  });

  test('создание + отмена продажи', async ({ request }) => {
    const createRes = await request.post(`${API_URL}/v1/sales`, {
      headers: authHeaders(trainerTokens),
      data: { clientId, amount: 2000, description: 'E2E: отмена' },
    });
    expect(createRes.ok()).toBeTruthy();
    const sale = await createRes.json();

    const cancelRes = await request.post(`${API_URL}/v1/sales/${sale.id}/cancel`, {
      headers: authHeaders(trainerTokens),
    });
    expect(cancelRes.ok()).toBeTruthy();
    expect((await cancelRes.json()).status).toBe('CANCELLED');
  });

  test('сводка долгов', async ({ request }) => {
    const response = await request.get(`${API_URL}/v1/sales/debts/summary`, {
      headers: authHeaders(trainerTokens),
    });

    expect(response.ok()).toBeTruthy();
    const summary = await response.json();
    expect(summary).toHaveProperty('totalAmount');
    expect(summary).toHaveProperty('count');
    expect(typeof summary.totalAmount).toBe('number');
  });

  test('список продаж с пагинацией', async ({ request }) => {
    const response = await request.get(`${API_URL}/v1/sales?limit=5`, {
      headers: authHeaders(trainerTokens),
    });

    expect(response.ok()).toBeTruthy();
    const body = await response.json();
    expect(body.data).toBeInstanceOf(Array);
    expect(body).toHaveProperty('hasMore');
  });
});

// -----------------------------------------------------------------------
// Funnel Advanced
// -----------------------------------------------------------------------
test.describe('MVP — Воронка (дополнительно)', () => {
  test('распределение по этапам', async ({ request }) => {
    const response = await request.get(`${API_URL}/v1/funnel/distribution`, {
      headers: authHeaders(coordTokens),
    });

    expect(response.ok()).toBeTruthy();
    const dist = await response.json();
    expect(dist).toBeInstanceOf(Array);
    if (dist.length > 0) {
      expect(dist[0]).toHaveProperty('stage');
      expect(dist[0]).toHaveProperty('count');
    }
  });

  test('отказ клиента с комментарием', async ({ request }) => {
    const uniquePhone = `+7801${Date.now().toString().slice(-7)}`;
    const clientRes = await request.post(`${API_URL}/v1/clients`, {
      headers: authHeaders(coordTokens),
      data: { firstName: 'Отказ', lastName: 'Тестовый', phone: uniquePhone },
    });
    const client = await clientRes.json();

    await request.post(`${API_URL}/v1/funnel/assign`, {
      headers: authHeaders(coordTokens),
      data: { clientId: client.id, trainerId, comment: 'Для отказа' },
    });

    const refuseRes = await request.post(`${API_URL}/v1/funnel/${client.id}/refuse`, {
      headers: authHeaders(trainerTokens),
      data: { comment: 'E2E: клиент отказался' },
    });

    expect(refuseRes.ok()).toBeTruthy();
    const entry = await refuseRes.json();
    expect(entry.stage).toBe('REFUSED');
  });

  test('конверсия воронки (координатор)', async ({ request }) => {
    const response = await request.get(`${API_URL}/v1/funnel/stats`, {
      headers: authHeaders(coordTokens),
    });

    expect(response.ok()).toBeTruthy();
    const stats = await response.json();
    expect(stats).toHaveProperty('newToAssigned');
    expect(typeof stats.newToAssigned).toBe('number');
  });
});

// -----------------------------------------------------------------------
// Trainer Rating & Coordinator
// -----------------------------------------------------------------------
test.describe('MVP — Рейтинг и координатор', () => {
  test('рейтинг тренеров', async ({ request }) => {
    const response = await request.get(`${API_URL}/v1/stats/rating`, {
      headers: authHeaders(trainerTokens),
    });

    expect(response.ok()).toBeTruthy();
    const body = await response.json();
    expect(body).toBeInstanceOf(Array);
  });

  test('координатор видит всех клиентов', async ({ request }) => {
    const response = await request.get(`${API_URL}/v1/clients`, {
      headers: authHeaders(coordTokens),
    });

    expect(response.ok()).toBeTruthy();
    const body = await response.json();
    expect(body.data.length).toBeGreaterThan(0);
  });

  test('координатор видит все тренировки', async ({ request }) => {
    const response = await request.get(`${API_URL}/v1/trainings`, {
      headers: authHeaders(coordTokens),
    });

    expect(response.ok()).toBeTruthy();
    const body = await response.json();
    expect(body.data).toBeInstanceOf(Array);
  });

  test('координатор видит все этапы воронки', async ({ request }) => {
    const stages = ['NEW', 'ASSIGNED', 'APPOINTMENT_SET', 'COMPLETED', 'REGULAR'];
    for (const stage of stages) {
      const response = await request.get(`${API_URL}/v1/funnel?stage=${stage}`, {
        headers: authHeaders(coordTokens),
      });
      expect(response.ok()).toBeTruthy();
    }
  });
});

// -----------------------------------------------------------------------
// Notifications, SIP, Clients Advanced
// -----------------------------------------------------------------------
test.describe('MVP — Уведомления, SIP, поиск', () => {
  test('список уведомлений', async ({ request }) => {
    const response = await request.get(`${API_URL}/v1/notifications`, {
      headers: authHeaders(trainerTokens),
    });

    expect(response.ok()).toBeTruthy();
    const body = await response.json();
    expect(body.data).toBeInstanceOf(Array);
  });

  test('журнал SIP-звонков', async ({ request }) => {
    const response = await request.get(`${API_URL}/v1/sip/calls`, {
      headers: authHeaders(trainerTokens),
    });

    // Module may not be enabled → 403 acceptable
    expect([200, 403]).toContain(response.status());
    if (response.ok()) {
      const body = await response.json();
      expect(body.data).toBeInstanceOf(Array);
    }
  });

  test('поиск клиентов по имени', async ({ request }) => {
    const response = await request.get(`${API_URL}/v1/clients?search=Анна`, {
      headers: authHeaders(coordTokens),
    });

    expect(response.ok()).toBeTruthy();
    const body = await response.json();
    expect(body.data).toBeInstanceOf(Array);
  });
});
