import { DocumentApi } from '../../api/documentApi';
import * as sinon from 'sinon';

describe('DocumentApi Unit Test', () => {
    let documentApiStub: sinon.SinonStubbedInstance<DocumentApi> & {
        sendDocumentFromTemplate: sinon.SinonStub;
    };

    beforeEach(() => {
        documentApiStub = {
            ...sinon.createStubInstance(DocumentApi),
            sendDocumentFromTemplate: sinon.stub()
        };
    });

    afterEach(() => {
        sinon.restore();
    });

    it('should mock and verify sendDocument call with full SendForSign data', async () => {
        const sendForSignData = {
            document_id: "12345",
            title: "Sample Document",
            message: "Please sign this document.",
            roles: [],
            brand_id: "brand123",
            labels: ["label1", "label2"],
            disable_emails: false,
            disable_sms: false,
            hide_document_id: false,
            expiry_days: 30,
            expiry_date_type: "Days",
            expiry_value: 60,
            enable_print_and_sign: true,
            enable_reassign: true,
            enable_signing_order: true,
            use_text_tags: false,
            text_tag_definitions: [],
            document_info: [],
            on_behalf_of: null,
            is_sandbox: false,
            role_removal_indices: [],
            document_download_option: "Combined",
            recipient_notification_settings: null,
            signers: [
                {
                    name: "John Doe",
                    email: "john.doe@example.com"
                }
            ]
        };

        const mockSendResponse = {
            documentId: "12345",
            status: "sent",
            message: "Document has been sent successfully."
        };

        documentApiStub.sendDocument.resolves(mockSendResponse);

        const result = await documentApiStub.sendDocument(sendForSignData);

        expect(result).toBeDefined();
        expect(result.documentId).toBe("12345");
        expect(result.status).toBe("sent");
        expect(result.message).toBe("Document has been sent successfully.");

        sinon.assert.calledOnceWithExactly(documentApiStub.sendDocument, sendForSignData);
    });

    it('should mock and verify sendDocumentFromTemplate call', async () => {
        const templateId = 'template789';
        const sendFromTemplateData = {
            title: 'Template Based Document',
            message: 'Please sign this document generated from a template.',
            roles: [
                {
                    role: 'Signer',
                    name: 'Alice Smith',
                    email: 'alice.smith@example.com'
                }
            ],
            brand_id: 'brand123',
            labels: ['template-doc'],
            disable_emails: false,
            disable_sms: false,
            hide_document_id: false,
            expiry_days: 15,
            expiry_date_type: 'Days',
            expiry_value: 15,
            enable_print_and_sign: false,
            enable_reassign: true,
            enable_signing_order: false,
            use_text_tags: false,
            text_tag_definitions: [],
            on_behalf_of: null,
            is_sandbox: false,
            document_download_option: 'Combined',
            recipient_notification_settings: null
        };

        const mockSendFromTemplateResponse = {
            documentId: 'generatedDocFromTemplate',
            status: 'sent',
            message: 'Document from template has been sent successfully.'
        };

        documentApiStub.sendDocumentFromTemplate.resolves(mockSendFromTemplateResponse);

        const result = await documentApiStub.sendDocumentFromTemplate(templateId, sendFromTemplateData);

        expect(result).toBeDefined();
        expect(result.documentId).toBe('generatedDocFromTemplate');
        expect(result.status).toBe('sent');
        expect(result.message).toBe('Document from template has been sent successfully.');

        sinon.assert.calledOnceWithExactly(documentApiStub.sendDocumentFromTemplate, templateId, sendFromTemplateData);
    });

    it('should mock and verify listDocuments call', async () => {
        const mockListResponse = {
            documents: [
                {
                    documentId: 'doc1',
                    title: 'Document 1',
                    status: 'sent',
                    createdAt: '2025-05-18T10:00:00Z',
                    sender: 'sender@example.com',
                    recipient: 'recipient@example.com',
                },
                {
                    documentId: 'doc2',
                    title: 'Document 2',
                    status: 'signed',
                    createdAt: '2025-05-19T11:00:00Z',
                    sender: 'sender@example.com',
                    recipient: 'recipient@example.com',
                },
            ],
            totalCount: 2,
            pageSize: 10,
            currentPage: 1,
        };

        documentApiStub.listDocuments.resolves(mockListResponse);

        const result = await documentApiStub.listDocuments({ page: 1, pageSize: 10 });

        expect(result).toBeDefined();
        expect(result.documents.length).toBe(2);
        expect(result.documents[0].documentId).toBe('doc1');
        expect(result.documents[1].status).toBe('signed');
        expect(result.totalCount).toBe(2);

        sinon.assert.calledOnceWithExactly(documentApiStub.listDocuments, { page: 1, pageSize: 10 });
    });

    it('should mock and verify downloadDocument call', async () => {
        const documentId = 'doc123';

        const mockDownloadResponse = Buffer.from('PDF file content');

        documentApiStub.downloadDocument = sinon.stub().resolves(mockDownloadResponse);

        const result = await documentApiStub.downloadDocument(documentId);

        expect(result).toBeDefined();
        expect(result).toBeInstanceOf(Buffer);
        expect(result.toString()).toBe('PDF file content');

        sinon.assert.calledOnceWithExactly(documentApiStub.downloadDocument, documentId);
    });

    it('should mock and verify sendReminder call', async () => {
        const documentId = 'doc123';
        const reminderData = {
            message: 'Reminder to sign the document.',
            recipients: ['john.doe@example.com'],
        };
    
        const mockReminderResponse = {
            success: true,
            message: 'Reminder sent successfully.',
            documentId: 'doc123',
        };

        documentApiStub.sendReminder = sinon.stub().resolves(mockReminderResponse);

        const result = await documentApiStub.sendReminder(documentId, reminderData);

        expect(result).toBeDefined();
        expect(result.success).toBe(true);
        expect(result.message).toBe('Reminder sent successfully.');
        expect(result.documentId).toBe(documentId);

        sinon.assert.calledOnceWithExactly(documentApiStub.sendReminder, documentId, reminderData);
    });

    it('should mock and verify revokeDocument call', async () => {
        const documentId = 'doc123';
        const revokeData = {
            reason: 'Document contains incorrect information.'
        };
    
        const mockRevokeResponse = {
            success: true,
            message: 'Document has been revoked successfully.',
            documentId: 'doc123'
        };

        documentApiStub.revokeDocument = sinon.stub().resolves(mockRevokeResponse);

        const result = await documentApiStub.revokeDocument(documentId, revokeData);

        expect(result).toBeDefined();
        expect(result.success).toBe(true);
        expect(result.message).toBe('Document has been revoked successfully.');
        expect(result.documentId).toBe(documentId);

        sinon.assert.calledOnceWithExactly(documentApiStub.revokeDocument, documentId, revokeData);
    });

    it('should mock and verify addDocumentTag call', async () => {
        const documentId = 'doc123';
        const tagData = {
            tags: ['urgent', 'review']
        };
    
        const mockAddTagResponse = {
            success: true,
            message: 'Tags added to document successfully.',
            documentId: 'doc123',
            tags: ['urgent', 'review']
        };

        documentApiStub.addDocumentTag = sinon.stub().resolves(mockAddTagResponse);

        const result = await documentApiStub.addDocumentTag(documentId, tagData);

        expect(result).toBeDefined();
        expect(result.success).toBe(true);
        expect(result.message).toBe('Tags added to document successfully.');
        expect(result.documentId).toBe(documentId);
        expect(result.tags).toEqual(['urgent', 'review']);

        sinon.assert.calledOnceWithExactly(documentApiStub.addDocumentTag, documentId, tagData);
    });

    it('should mock and verify deleteDocumentTag call', async () => {
        const documentId = 'doc123';
        const tagData = {
            tags: ['urgent', 'review']
        };

        const mockDeleteTagResponse = {
            success: true,
            message: 'Tags deleted from document successfully.',
            documentId: 'doc123',
            tags: []
        };

        documentApiStub.deleteDocumentTag = sinon.stub().resolves(mockDeleteTagResponse);

        const result = await documentApiStub.deleteDocumentTag(documentId, tagData);

        expect(result).toBeDefined();
        expect(result.success).toBe(true);
        expect(result.message).toBe('Tags deleted from document successfully.');
        expect(result.documentId).toBe(documentId);
        expect(result.tags).toEqual([]);

        sinon.assert.calledOnceWithExactly(documentApiStub.deleteDocumentTag, documentId, tagData);
    });

    it('should mock and verify prefillFormFields call', async () => {
        const documentId = 'doc123';
        const formData = {
            title: 'Sample Document',
            message: 'Please review and sign this document.',
            formFields: {
                'FirstName': 'John',
                'LastName': 'Doe',
                'Email': 'john.doe@example.com',
                'Phone': '123-456-7890'
            },
            signers: [
                {
                    name: 'John Doe',
                    email: 'john.doe@example.com',
                    role: 'Signer'
                }
            ]
        };
    
        const mockPrefillResponse = {
            documentId: 'doc123',
            status: 'prefilled',
            message: 'Document fields have been prefilled successfully.',
            prefilledFields: formData.formFields
        };

        documentApiStub.prefillFormFields = sinon.stub().resolves(mockPrefillResponse);

        const result = await documentApiStub.prefillFormFields(documentId, formData);

        expect(result).toBeDefined();
        expect(result.documentId).toBe('doc123');
        expect(result.status).toBe('prefilled');
        expect(result.message).toBe('Document fields have been prefilled successfully.');
        expect(result.prefilledFields).toEqual(formData.formFields);

        sinon.assert.calledOnceWithExactly(documentApiStub.prefillFormFields, documentId, formData);
    });

    it('should mock and verify downloadAuditLog call', async () => {
        const documentId = 'doc123';
        const mockAuditLogResponse = {
            documentId: 'doc123',
            status: 'downloaded',
            message: 'Audit log has been downloaded successfully.',
            auditLogUrl: 'https://example.com/audit/logs/doc123-audit-log.pdf'
        };

        documentApiStub.downloadAuditLog = sinon.stub().resolves(mockAuditLogResponse);

        const result = await documentApiStub.downloadAuditLog(documentId);

        expect(result).toBeDefined();
        expect(result.documentId).toBe('doc123');
        expect(result.status).toBe('downloaded');
        expect(result.message).toBe('Audit log has been downloaded successfully.');
        expect(result.auditLogUrl).toBe('https://example.com/audit/logs/doc123-audit-log.pdf');

        sinon.assert.calledOnceWithExactly(documentApiStub.downloadAuditLog, documentId);
    });

    it('should mock and verify changeAccessCode call', async () => {
        const documentId = 'doc123';
        const newAccessCode = 'newAccessCode123';
        const mockChangeAccessCodeResponse = {
            documentId: 'doc123',
            status: 'access code updated',
            message: 'The access code has been successfully changed.',
            newAccessCode: 'newAccessCode123'
        };

        documentApiStub.changeAccessCode = sinon.stub().resolves(mockChangeAccessCodeResponse);

        const result = await documentApiStub.changeAccessCode(documentId, newAccessCode);

        expect(result).toBeDefined();
        expect(result.documentId).toBe('doc123');
        expect(result.status).toBe('access code updated');
        expect(result.message).toBe('The access code has been successfully changed.');
        expect(result.newAccessCode).toBe('newAccessCode123');

        sinon.assert.calledOnceWithExactly(documentApiStub.changeAccessCode, documentId, newAccessCode);
    });

    it('should mock and verify changeRecipients call', async () => {
        const documentId = 'doc123';
        const newRecipients = [
            { name: 'Alice Doe', email: 'alice.doe@example.com' },
            { name: 'Bob Smith', email: 'bob.smith@example.com' }
        ];
    
        const mockChangeRecipientsResponse = {
            documentId: 'doc123',
            status: 'recipients updated',
            message: 'Recipients have been successfully updated.',
            recipients: newRecipients
        };

        documentApiStub.changeRecipients = sinon.stub().resolves(mockChangeRecipientsResponse);

        const result = await documentApiStub.changeRecipients(documentId, newRecipients);

        expect(result).toBeDefined();
        expect(result.documentId).toBe('doc123');
        expect(result.status).toBe('recipients updated');
        expect(result.message).toBe('Recipients have been successfully updated.');
        expect(result.recipients).toEqual(newRecipients);

        sinon.assert.calledOnceWithExactly(documentApiStub.changeRecipients, documentId, newRecipients);
    });

    it('should mock and verify getEmbeddedSigningLink call', async () => {
        const documentId = 'doc123';
        const signerId = 'signer123';
    
        const mockEmbeddedSigningLinkResponse = {
            documentId: 'doc123',
            signerId: 'signer123',
            embeddedSignUrl: 'https://signing-platform.com/embed/sign/document/12345'
        };

        documentApiStub.getEmbeddedSigningLink = sinon.stub().resolves(mockEmbeddedSigningLinkResponse);
    
        const result = await documentApiStub.getEmbeddedSigningLink(documentId, signerId);
    
        expect(result).toBeDefined();
        expect(result.documentId).toBe('doc123');
        expect(result.signerId).toBe('signer123');
        expect(result.embeddedSignUrl).toBe('https://signing-platform.com/embed/sign/document/12345');
    
        sinon.assert.calledOnceWithExactly(documentApiStub.getEmbeddedSigningLink, documentId, signerId);
    });

    it('should mock and verify addAuthentication call', async () => {
        const documentId = 'doc123';
        const authenticationData = {
            authenticationMethod: 'password',
            password: 'securePassword123',
            enableMFA: true,
            mfaMethod: 'sms',
        };
    
        const mockAuthenticationResponse = {
            documentId: 'doc123',
            status: 'authentication_added',
            message: 'Authentication method added successfully.'
        };
    
        documentApiStub.addAuthentication.resolves(mockAuthenticationResponse);
    
        const result = await documentApiStub.addAuthentication(documentId, authenticationData);
    
        expect(result).toBeDefined();
        expect(result.documentId).toBe('doc123');
        expect(result.status).toBe('authentication_added');
        expect(result.message).toBe('Authentication method added successfully.');
    
        sinon.assert.calledOnceWithExactly(documentApiStub.addAuthentication, documentId, authenticationData);
    });
    
    it('should mock and verify removeAuthentication call', async () => {
        const documentId = 'doc123';
    
        const mockRemoveAuthResponse = {
            documentId: 'doc123',
            status: 'authentication_removed',
            message: 'Authentication method removed successfully.'
        };
    
        documentApiStub.removeAuthentication.resolves(mockRemoveAuthResponse);
    
        const result = await documentApiStub.removeAuthentication(documentId);
    
        expect(result).toBeDefined();
        expect(result.documentId).toBe('doc123');
        expect(result.status).toBe('authentication_removed');
        expect(result.message).toBe('Authentication method removed successfully.');
    
        sinon.assert.calledOnceWithExactly(documentApiStub.removeAuthentication, documentId);
    });

    
    it('should mock and verify extendDocumentExpiry call', async () => {
        const documentId = 'doc123';
        const newExpiryDate = '2025-06-01';
    
        const mockExtendExpiryResponse = {
            documentId: 'doc123',
            status: 'expiry_extended',
            newExpiryDate: newExpiryDate,
            message: 'Document expiry extended successfully.'
        };
    
        documentApiStub.extendDocumentExpiry = sinon.stub().resolves(mockExtendExpiryResponse);
    
        const result = await documentApiStub.extendDocumentExpiry(documentId, newExpiryDate);
    
        expect(result).toBeDefined();
        expect(result.documentId).toBe('doc123');
        expect(result.status).toBe('expiry_extended');
        expect(result.newExpiryDate).toBe(newExpiryDate);
        expect(result.message).toBe('Document expiry extended successfully.');
    
        sinon.assert.calledOnceWithExactly(documentApiStub.extendDocumentExpiry, documentId, newExpiryDate);
    });

    it('should mock and verify listBehalfDocuments call', async () => {
        const onBehalfOf = 'john.doe@example.com';

        const mockBehalfDocuments = [
            {
                documentId: 'doc123',
                title: 'Sample Document 1',
                status: 'signed',
                onBehalfOf: 'john.doe@example.com',
            },
            {
                documentId: 'doc124',
                title: 'Sample Document 2',
                status: 'pending',
                onBehalfOf: 'john.doe@example.com',
            },
        ];

        documentApiStub.listBehalfDocuments = sinon.stub().resolves(mockBehalfDocuments);

        const result = await documentApiStub.listBehalfDocuments(onBehalfOf);

        expect(result).toBeDefined();
        expect(result.length).toBe(2);
        expect(result[0].documentId).toBe('doc123');
        expect(result[0].status).toBe('signed');
        expect(result[1].status).toBe('pending');
        expect(result[1].title).toBe('Sample Document 2');

        sinon.assert.calledOnceWithExactly(documentApiStub.listBehalfDocuments, onBehalfOf);
    });

    it('should mock and verify listTeamDocuments call', async () => {
        const teamId = 'team123';

        const mockTeamDocuments = [
            {
                documentId: 'doc123',
                title: 'Team Document 1',
                status: 'signed',
                teamId: 'team123',
            },
            {
                documentId: 'doc124',
                title: 'Team Document 2',
                status: 'pending',
                teamId: 'team123',
            },
        ];

        documentApiStub.listTeamDocuments = sinon.stub().resolves(mockTeamDocuments);

        const result = await documentApiStub.listTeamDocuments(teamId);

        expect(result).toBeDefined();
        expect(result.length).toBe(2);
        expect(result[0].documentId).toBe('doc123');
        expect(result[0].status).toBe('signed');
        expect(result[1].status).toBe('pending');
        expect(result[1].title).toBe('Team Document 2');

        sinon.assert.calledOnceWithExactly(documentApiStub.listTeamDocuments, teamId);
    });
});