// #region GitServiceContractTests [C:3] [TYPE Module] [SEMANTICS git, service, test, contract, api]
// @BRIEF API client tests ensuring correct endpoints are called per contract for Git service operations.
// @RELATION DEPENDS_ON -> [EXT:frontend:GitServiceClient]

// #region gitServiceContractTests:Module [TYPE Function]
// @SEMANTICS: git-service, api-client, contract-tests
// @RELATION DEPENDS_ON -> [EXT:frontend:GitServiceClient]
// @PURPOSE: API client tests ensuring correct endpoints are called per contract
// @LAYER Tests

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { gitService } from '../gitService';
import { requestApi } from '../../lib/api';

vi.mock('../../lib/api', () => ({
    requestApi: vi.fn()
}));

describe('gitService', () => {
    beforeEach(() => {
        vi.clearAllMocks();
    });

    describe('Config Management', () => {
        // @PRE User authenticated
        // @POST Returns list of Git configs
        it('getConfigs calls /git/config', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue([]);
            const result = await gitService.getConfigs();
            expect(requestApi).toHaveBeenCalledWith('/git/config', 'GET', null, { suppressToast: true });
            expect(result).toEqual([]);
        });

        // @PRE Config object must be valid
        // @POST New config created and returned
        it('createConfig calls POST /git/config', async () => {
            const config = { name: 'Test', provider: 'GITHUB' };
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({ id: '1', ...config });
            const result = await gitService.createConfig(config);
            expect(requestApi).toHaveBeenCalledWith('/git/config', 'POST', config, { suppressToast: true });
            expect(result.id).toBe('1');
        });

        // @PRE configId must exist
        // @POST Config deleted
        it('deleteConfig calls DELETE /git/config/:id', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({ status: 'success' });
            await gitService.deleteConfig('config-123');
            expect(requestApi).toHaveBeenCalledWith('/git/config/config-123', 'DELETE', null, { suppressToast: true });
        });

        // @PRE configId must exist
        // @POST Config updated
        it('updateConfig calls PUT /git/config/:id', async () => {
            const config = { name: 'Updated' };
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({ id: 'config-123', ...config });
            await gitService.updateConfig('config-123', config);
            expect(requestApi).toHaveBeenCalledWith('/git/config/config-123', 'PUT', config, { suppressToast: true });
        });

        // @PRE Config must contain valid URL and PAT
        // @POST Returns connection status
        it('testConnection calls POST /git/config/test', async () => {
            const config = { url: 'https://github.com', pat: 'secret' };
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({ status: 'success' });
            await gitService.testConnection(config);
            expect(requestApi).toHaveBeenCalledWith('/git/config/test', 'POST', config, { suppressToast: true });
        });
    });

    describe('Remote Repository Management', () => {
        // @PRE configId references GITEA
        // @POST Returns repo metadata
        it('listGiteaRepositories calls GET /git/config/:id/gitea/repos', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue([]);
            await gitService.listGiteaRepositories('conf-1');
            expect(requestApi).toHaveBeenCalledWith('/git/config/conf-1/gitea/repos', 'GET', null, { suppressToast: true });
        });

        // @PRE configId references GITEA
        // @POST Repo created on Gitea
        it('createGiteaRepository calls POST /git/config/:id/gitea/repos', async () => {
            const payload = { name: 'new-repo' };
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({ name: 'new-repo' });
            await gitService.createGiteaRepository('conf-1', payload);
            expect(requestApi).toHaveBeenCalledWith('/git/config/conf-1/gitea/repos', 'POST', payload, { suppressToast: true });
        });

        // @PRE configId references supported provider
        // @POST Remote repo created
        it('createRemoteRepository calls POST /git/config/:id/repositories', async () => {
            const payload = { name: 'new-remote' };
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({ name: 'new-remote' });
            await gitService.createRemoteRepository('conf-1', payload);
            expect(requestApi).toHaveBeenCalledWith('/git/config/conf-1/repositories', 'POST', payload, { suppressToast: true });
        });

        // @PRE configId references GITEA
        // @POST Repo deleted on Gitea
        it('deleteGiteaRepository calls DELETE with encoded owner and repo', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({ status: 'success' });
            await gitService.deleteGiteaRepository('conf-1', 'my-org', 'my-repo');
            expect(requestApi).toHaveBeenCalledWith('/git/config/conf-1/gitea/repos/my-org/my-repo', 'DELETE', null, { suppressToast: true });
        });
    });

    describe('Dashboard Repository Operations', () => {
        // @PRE Dashboard exists and config_id valid
        // @POST Repo initialized
        it('initRepository calls POST with remote_url and config_id', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({});
            await gitService.initRepository(123, 'conf-1', 'http://remote.git');
            expect(requestApi).toHaveBeenCalledWith('/git/repositories/123/init', 'POST', {
                config_id: 'conf-1', remote_url: 'http://remote.git'
            }, { suppressToast: true });
        });

        // @PRE Repo initialized
        // @POST Returns binding
        it('getRepositoryBinding calls GET', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({});
            await gitService.getRepositoryBinding('dash-slug');
            expect(requestApi).toHaveBeenCalledWith('/git/repositories/dash-slug', 'GET', null, { suppressToast: true });
        });

        // @PRE Repo initialized
        // @POST Returns branches
        it('getBranches calls GET /branches', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue([]);
            await gitService.getBranches(123);
            expect(requestApi).toHaveBeenCalledWith('/git/repositories/123/branches', 'GET', null, { suppressToast: true });
        });

        // @PRE Source branch exists
        // @POST New branch created
        it('createBranch calls POST /branches with name and from_branch', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({});
            await gitService.createBranch(123, 'feature', 'main');
            expect(requestApi).toHaveBeenCalledWith('/git/repositories/123/branches', 'POST', {
                name: 'feature', from_branch: 'main'
            }, { suppressToast: true });
        });

        // @PRE Target branch exists
        // @POST Repo checked out
        it('checkoutBranch calls POST /checkout', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({});
            await gitService.checkoutBranch(123, 'feature');
            expect(requestApi).toHaveBeenCalledWith('/git/repositories/123/checkout', 'POST', { name: 'feature' }, { suppressToast: true });
        });

        // @PRE Message not empty
        // @POST Changes committed
        it('commit calls POST /commit with message and files', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({});
            await gitService.commit(123, 'Initial commit', ['file.txt']);
            expect(requestApi).toHaveBeenCalledWith('/git/repositories/123/commit', 'POST', {
                message: 'Initial commit', files: ['file.txt']
            }, { suppressToast: true });
        });

        // @PRE Remote configured
        // @POST Remote updated
        it('push calls POST /push', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({});
            await gitService.push(123);
            expect(requestApi).toHaveBeenCalledWith('/git/repositories/123/push', 'POST', null, { suppressToast: true });
        });

        // @PRE Dashboard ref valid
        // @POST Repo deleted
        it('deleteRepository calls DELETE', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({});
            await gitService.deleteRepository(123);
            expect(requestApi).toHaveBeenCalledWith('/git/repositories/123', 'DELETE', null, { suppressToast: true });
        });

        // @PRE Remote configured
        // @POST Local repo updated
        it('pull calls POST /pull', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({});
            await gitService.pull(123);
            expect(requestApi).toHaveBeenCalledWith('/git/repositories/123/pull', 'POST', null, { suppressToast: true });
        });
    });

    describe('Other Operations', () => {
        // @POST Returns list of environments
        it('getEnvironments calls GET /environments', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue([]);
            await gitService.getEnvironments();
            expect(requestApi).toHaveBeenCalledWith('/git/environments', 'GET', null, { suppressToast: true });
        });

        // @PRE Environment active
        // @POST Dashboard deployed
        it('deploy calls POST /deploy with environment_id', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({});
            await gitService.deploy(123, 'env-1');
            expect(requestApi).toHaveBeenCalledWith('/git/repositories/123/deploy', 'POST', { environment_id: 'env-1' }, { suppressToast: true });
        });

        // @POST Returns commit history
        it('getHistory calls GET /history with limit', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue([]);
            await gitService.getHistory(123, 10);
            expect(requestApi).toHaveBeenCalledWith('/git/repositories/123/history?limit=10', 'GET', null, { suppressToast: true });
        });

        // @POST Syncs local and remote
        it('sync calls POST /sync with optional params', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({});
            await gitService.sync(123, 'env-source', 'env-target');
            expect(requestApi).toHaveBeenCalledWith('/git/repositories/123/sync?source_env_id=env-source&env_id=env-target', 'POST', null, { suppressToast: true });
        });

        // @PRE Dashboard id valid
        // @POST Returns status
        it('getStatus calls GET /status', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({});
            await gitService.getStatus(123);
            expect(requestApi).toHaveBeenCalledWith('/git/repositories/123/status', 'GET', null, { suppressToast: true });
        });

        // @PRE Dashboard ids array
        // @POST Returns batch statuses
        it('getStatusesBatch calls POST /status/batch', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({});
            await gitService.getStatusesBatch([1, 2, 3]);
            expect(requestApi).toHaveBeenCalledWith('/git/repositories/status/batch', 'POST', { dashboard_ids: [1, 2, 3] }, { suppressToast: true });
        });

        // @PRE Dashboard id valid
        // @POST Returns diff
        it('getDiff calls GET /diff with params', async () => {
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue('');
            await gitService.getDiff(123, 'file.txt', true, 'env-1');
            expect(requestApi).toHaveBeenCalledWith('/git/repositories/123/diff?file_path=file.txt&staged=true&env_id=env-1', 'GET', null, { suppressToast: true });
        });

        // @PRE Repo initialized
        // @POST Returns promotion metadata
        it('promote calls POST /promote with payload', async () => {
            const payload = { from_branch: 'dev', to_branch: 'main', mode: 'mr' };
            (requestApi as ReturnType<typeof vi.fn>).mockResolvedValue({});
            await gitService.promote(123, payload);
            expect(requestApi).toHaveBeenCalledWith('/git/repositories/123/promote', 'POST', payload, { suppressToast: true });
        });
    });
});
// #endregion gitServiceContractTests:Module
// #endregion GitServiceContractTests
