import test from 'node:test';
import assert from 'node:assert/strict';

import {
    CONFLICT_CHECK_REASON,
    CONFLICT_CHECK_STATE,
    conflictCheckPresentation,
    currentWeekDateRange,
    formatLocalDate,
    loadDepartmentSchedules
} from '../admin/js/dashboard-conflicts.js';

process.env.TZ = 'Europe/Moscow';

test('неделя для 2026-07-02 охватывает 2026-06-29…2026-07-05', () => {
    assert.deepEqual(
        currentWeekDateRange(new Date('2026-07-01T21:15:00Z')),
        { startDate: '2026-06-29', endDate: '2026-07-05' }
    );
});

test('неделя корректно пересекает границу года', () => {
    assert.deepEqual(
        currentWeekDateRange(new Date(2026, 0, 1, 8, 30, 0)),
        { startDate: '2025-12-29', endDate: '2026-01-04' }
    );
});

test('date-only формируется из локальных компонентов', () => {
    assert.equal(formatLocalDate(new Date('2026-01-01T21:15:00Z')), '2026-01-02');
});

test('полный успех разрешает достоверный вывод по всем кафедрам', async () => {
    const result = await loadDepartmentSchedules(
        [{ id: 1 }, { id: 2 }],
        async department => [{ departmentId: department.id }]
    );

    assert.equal(result.state, CONFLICT_CHECK_STATE.COMPLETE);
    assert.equal(result.checkedDepartmentCount, 2);
    assert.equal(result.totalDepartmentCount, 2);
    assert.deepEqual(result.lessons, [{ departmentId: 1 }, { departmentId: 2 }]);
    assert.deepEqual(result.failedDepartments, []);
});

test('единичный отказ даёт частичный результат, а не пустой успешный ответ', async () => {
    const departments = [
        { id: 1, departmentName: 'Физика' },
        { id: 2, departmentName: 'Математика' }
    ];
    const result = await loadDepartmentSchedules(departments, async department => {
        if (department.id === 2) {
            throw new Error('тестовая техническая причина');
        }
        return [{ departmentId: department.id }];
    });

    assert.equal(result.state, CONFLICT_CHECK_STATE.PARTIAL);
    assert.equal(result.checkedDepartmentCount, 1);
    assert.deepEqual(result.lessons, [{ departmentId: 1 }]);
    assert.deepEqual(result.failedDepartments, [{ id: 2, name: 'Математика' }]);
    assert.equal('reason' in result.failedDepartments[0], false);
});

test('отказ всех запросов означает, что проверка не выполнена', async () => {
    const result = await loadDepartmentSchedules(
        [{ id: 1 }, { id: 2 }],
        async () => { throw new Error('тестовый отказ'); }
    );

    assert.equal(result.state, CONFLICT_CHECK_STATE.NOT_RUN);
    assert.equal(result.reason, CONFLICT_CHECK_REASON.ALL_REQUESTS_FAILED);
    assert.equal(result.checkedDepartmentCount, 0);
    assert.deepEqual(result.lessons, []);
});

test('отсутствие кафедр не изображается как зелёная успешная проверка', async () => {
    const result = await loadDepartmentSchedules([], async () => []);

    assert.equal(result.state, CONFLICT_CHECK_STATE.NOT_RUN);
    assert.equal(result.reason, CONFLICT_CHECK_REASON.NO_DEPARTMENTS);
});

test('зелёная карточка разрешена только для полной проверки без конфликтов', () => {
    const complete = {
        state: CONFLICT_CHECK_STATE.COMPLETE,
        checkedDepartmentCount: 2,
        totalDepartmentCount: 2
    };
    const range = { startDate: '2026-06-29', endDate: '2026-07-05' };

    assert.equal(conflictCheckPresentation(complete, 0, range).tone, 'success');
    assert.equal(conflictCheckPresentation(complete, 1, range), null);
});

test('некорректный контракт результата не может породить зелёный статус', () => {
    const range = { startDate: '2026-06-29', endDate: '2026-07-05' };

    assert.throws(
        () => conflictCheckPresentation({
            state: CONFLICT_CHECK_STATE.COMPLETE,
            checkedDepartmentCount: 1,
            totalDepartmentCount: 2
        }, 0, range),
        /Полная проверка должна охватывать все кафедры/
    );
    assert.throws(
        () => conflictCheckPresentation({
            state: CONFLICT_CHECK_STATE.COMPLETE,
            checkedDepartmentCount: 2,
            totalDepartmentCount: 2
        }, Number.NaN, range),
        /Количество конфликтов должно быть целым неотрицательным числом/
    );
});

test('частичная проверка без конфликтов остаётся предупреждением', () => {
    const presentation = conflictCheckPresentation({
        state: CONFLICT_CHECK_STATE.PARTIAL,
        checkedDepartmentCount: 1,
        totalDepartmentCount: 2
    }, 0, { startDate: '2026-06-29', endDate: '2026-07-05' });

    assert.equal(presentation.tone, 'warning');
    assert.equal(presentation.title, 'Проверка выполнена частично');
    assert.match(presentation.description, /во всём расписании нельзя/);
});

test('частичная проверка с конфликтами предупреждает о неполноте результата', () => {
    const presentation = conflictCheckPresentation({
        state: CONFLICT_CHECK_STATE.PARTIAL,
        checkedDepartmentCount: 1,
        totalDepartmentCount: 3
    }, 2, { startDate: '2026-06-29', endDate: '2026-07-05' });

    assert.equal(presentation.tone, 'warning');
    assert.match(presentation.description, /Показаны найденные конфликты/);
});

test('полный отказ формирует явное состояние «Проверка не выполнена»', () => {
    const presentation = conflictCheckPresentation({
        state: CONFLICT_CHECK_STATE.NOT_RUN,
        reason: CONFLICT_CHECK_REASON.ALL_REQUESTS_FAILED,
        checkedDepartmentCount: 0,
        totalDepartmentCount: 2
    }, 0, { startDate: '2026-06-29', endDate: '2026-07-05' });

    assert.equal(presentation.tone, 'error');
    assert.equal(presentation.title, 'Проверка не выполнена');
    assert.match(presentation.description, /Подтвердить отсутствие конфликтов нельзя/);
});
