import { describe, it, expect, beforeEach, vi } from 'vitest';
import { mount, config } from '@vue/test-utils';
import { createPinia, setActivePinia } from 'pinia';
import { createVuetify } from 'vuetify';
import axios from 'axios';
import ProjectDetailsDrawer from '../../resources/js/components/projects/ProjectDetailsDrawer.vue';
import type { Project } from '../../resources/js/stores/projectsStore';
import { useProjectsStore } from '../../resources/js/stores/projectsStore';

vi.mock('axios');

// VAutocomplete (added in regions field task) requires Vuetify defaults provide context.
// Register Vuetify plugin globally for all mount() calls in this file.
config.global.plugins = [createVuetify()];

const sampleProject: Project = {
    id: 42,
    name: 'Натяжные потолки',
    signal_type: 'call',
    signal_identifier: '79161112233',
    daily_limit_target: 30,
    delivered_today: 0,
    is_active: true,
    archived_at: null,
    region_mask: 0,
    region_mode: 'include',
    delivery_days_mask: 31, // Mon-Fri
    sync_status: 'pending',
};

describe('ProjectDetailsDrawer', () => {
    beforeEach(() => setActivePinia(createPinia()));

    it('does not have .open class when project=null', () => {
        const wrapper = mount(ProjectDetailsDrawer, { props: { project: null } });
        const aside = wrapper.find('aside.project-details-drawer');
        expect(aside.exists()).toBe(true);
        expect(aside.classes()).not.toContain('open');
    });

    it('renders project name + daily_limit_target + delivery_days', () => {
        const wrapper = mount(ProjectDetailsDrawer, { props: { project: sampleProject } });
        const aside = wrapper.find('aside.project-details-drawer');
        expect(aside.classes()).toContain('open');
        expect(wrapper.text()).toContain('Натяжные потолки');
        const nameInput = wrapper.get('input[data-testid="pdd-name"]');
        expect((nameInput.element as HTMLInputElement).value).toBe('Натяжные потолки');
        const limitInput = wrapper.get('input[data-testid="pdd-limit"]');
        expect((limitInput.element as HTMLInputElement).value).toBe('30');
        // Days mask 31 = bits 0..4 = Mon..Fri (5 days active)
        const dayBtns = wrapper.findAll('button[data-testid^="pdd-day-"]');
        expect(dayBtns.length).toBe(7);
        const activeBtns = dayBtns.filter(b => b.classes().includes('active'));
        expect(activeBtns.length).toBe(5);
    });

    it('emits close on X button click', async () => {
        const wrapper = mount(ProjectDetailsDrawer, { props: { project: sampleProject } });
        await wrapper.get('[data-testid="pdd-close"]').trigger('click');
        expect(wrapper.emitted('close')).toHaveLength(1);
    });

    it('emits close on Cancel button click', async () => {
        const wrapper = mount(ProjectDetailsDrawer, { props: { project: sampleProject } });
        await wrapper.get('[data-testid="pdd-cancel"]').trigger('click');
        expect(wrapper.emitted('close')).toHaveLength(1);
    });

    it('emits close on ESC keydown when project is set', async () => {
        const wrapper = mount(ProjectDetailsDrawer, { props: { project: sampleProject }, attachTo: document.body });
        document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
        await wrapper.vm.$nextTick();
        expect(wrapper.emitted('close')).toHaveLength(1);
        wrapper.unmount();
    });

    it('does NOT emit close on ESC when project=null', async () => {
        const wrapper = mount(ProjectDetailsDrawer, { props: { project: null }, attachTo: document.body });
        document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
        await wrapper.vm.$nextTick();
        expect(wrapper.emitted('close')).toBeUndefined();
        wrapper.unmount();
    });

    it('reseeds form when project.id changes', async () => {
        const wrapper = mount(ProjectDetailsDrawer, { props: { project: sampleProject } });
        const nameInputBefore = wrapper.get('input[data-testid="pdd-name"]').element as HTMLInputElement;
        expect(nameInputBefore.value).toBe('Натяжные потолки');

        const other: Project = { ...sampleProject, id: 99, name: 'Окна СПб', daily_limit_target: 50 };
        await wrapper.setProps({ project: other });

        const nameInputAfter = wrapper.get('input[data-testid="pdd-name"]').element as HTMLInputElement;
        expect(nameInputAfter.value).toBe('Окна СПб');
        expect((wrapper.get('input[data-testid="pdd-limit"]').element as HTMLInputElement).value).toBe('50');
    });

    it('Save: PATCH /api/projects/{id} + emits saved on 200', async () => {
        (axios.patch as unknown as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ data: { data: sampleProject } });
        const wrapper = mount(ProjectDetailsDrawer, { props: { project: sampleProject } });

        await wrapper.get('[data-testid="pdd-name"]').setValue('Новое имя');
        await wrapper.get('[data-testid="pdd-save"]').trigger('click');
        await wrapper.vm.$nextTick();
        await wrapper.vm.$nextTick();

        expect(axios.patch).toHaveBeenCalledWith(
            '/api/projects/42',
            expect.objectContaining({ name: 'Новое имя', daily_limit_target: 30 }),
        );
        expect(wrapper.emitted('saved')).toHaveLength(1);
    });

    it('Save: 422 → errors reactive проставляется, no saved emit', async () => {
        (axios.patch as unknown as ReturnType<typeof vi.fn>).mockRejectedValueOnce({
            response: { status: 422, data: { errors: { name: ['Имя обязательно'] } } },
        });
        const wrapper = mount(ProjectDetailsDrawer, { props: { project: sampleProject } });

        await wrapper.get('[data-testid="pdd-save"]').trigger('click');
        await wrapper.vm.$nextTick();
        await wrapper.vm.$nextTick();

        expect(wrapper.emitted('saved')).toBeUndefined();
        expect(wrapper.text()).toContain('Имя обязательно');
    });

    it('Pause button calls store.toggleActive', async () => {
        (axios.patch as unknown as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ data: { data: { ...sampleProject, is_active: false } } });
        (axios.get as unknown as ReturnType<typeof vi.fn> | undefined)?.mockResolvedValue?.({ data: { data: [], meta: { total: 0 } } });
        const wrapper = mount(ProjectDetailsDrawer, { props: { project: sampleProject } });
        const store = useProjectsStore();
        const spy = vi.spyOn(store, 'toggleActive').mockResolvedValueOnce(undefined);

        await wrapper.get('[data-testid="pdd-pause"]').trigger('click');
        expect(spy).toHaveBeenCalledWith(sampleProject);
    });

    it('Pause button label is "Возобновить" when is_active=false', () => {
        const paused: Project = { ...sampleProject, is_active: false };
        const wrapper = mount(ProjectDetailsDrawer, { props: { project: paused } });
        expect(wrapper.get('[data-testid="pdd-pause"]').text()).toContain('Возобновить');
    });

    it('Pause button label is "Приостановить" when is_active=true', () => {
        const wrapper = mount(ProjectDetailsDrawer, { props: { project: sampleProject } });
        expect(wrapper.get('[data-testid="pdd-pause"]').text()).toContain('Приостановить');
    });

    it('Delete: confirm=true → archive + close emit', async () => {
        const wrapper = mount(ProjectDetailsDrawer, { props: { project: sampleProject } });
        const store = useProjectsStore();
        const spy = vi.spyOn(store, 'archive').mockResolvedValueOnce(undefined);
        vi.stubGlobal('confirm', () => true);

        await wrapper.get('[data-testid="pdd-delete"]').trigger('click');
        await wrapper.vm.$nextTick();
        expect(spy).toHaveBeenCalledWith(42);
        expect(wrapper.emitted('close')).toBeDefined();

        vi.unstubAllGlobals();
    });

    it('Delete: confirm=false → no archive, no close', async () => {
        const wrapper = mount(ProjectDetailsDrawer, { props: { project: sampleProject } });
        const store = useProjectsStore();
        const spy = vi.spyOn(store, 'archive').mockResolvedValueOnce(undefined);
        vi.stubGlobal('confirm', () => false);

        await wrapper.get('[data-testid="pdd-delete"]').trigger('click');
        expect(spy).not.toHaveBeenCalled();
        expect(wrapper.emitted('close')).toBeUndefined();

        vi.unstubAllGlobals();
    });

    it('renders region chips when project has non-zero region_mask', async () => {
        const withRegions: Project = { ...sampleProject, region_mask: 6, region_mode: 'exclude' };
        const wrapper = mount(ProjectDetailsDrawer, { props: { project: withRegions } });
        await wrapper.vm.$nextTick();
        const text = wrapper.text();
        expect(text).toContain('Адыгея');
        expect(text).toContain('Башкортостан');
    });

    it('selecting regions encodes mask + sets mode=exclude on save', async () => {
        (axios.patch as unknown as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ data: { data: sampleProject } });
        const wrapper = mount(ProjectDetailsDrawer, { props: { project: sampleProject } });
        const autocomplete = wrapper.getComponent({ name: 'VAutocomplete' });
        await autocomplete.vm.$emit('update:model-value', [3, 5]);
        await wrapper.vm.$nextTick();
        await wrapper.get('[data-testid="pdd-save"]').trigger('click');
        await wrapper.vm.$nextTick();
        await wrapper.vm.$nextTick();
        expect(axios.patch).toHaveBeenCalledWith(
            '/api/projects/42',
            expect.objectContaining({ region_mask: 40, region_mode: 'exclude' }),
        );
    });

    it('clearing all regions resets mask=0 + mode=include on save', async () => {
        (axios.patch as unknown as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ data: { data: sampleProject } });
        const withRegions: Project = { ...sampleProject, region_mask: 6, region_mode: 'exclude' };
        const wrapper = mount(ProjectDetailsDrawer, { props: { project: withRegions } });
        const autocomplete = wrapper.getComponent({ name: 'VAutocomplete' });
        await autocomplete.vm.$emit('update:model-value', []);
        await wrapper.vm.$nextTick();
        await wrapper.get('[data-testid="pdd-save"]').trigger('click');
        await wrapper.vm.$nextTick();
        await wrapper.vm.$nextTick();
        expect(axios.patch).toHaveBeenCalledWith(
            '/api/projects/42',
            expect.objectContaining({ region_mask: 0, region_mode: 'include' }),
        );
    });
});
