/* eslint-disable @typescript-eslint/no-explicit-any */
import { expect } from 'chai';
import * as fs from 'fs';
import * as path from 'path';

import { DlcAccept, DlcOffer, DlcSign, MessageType } from '../../lib';

interface DlcSpecsTestData {
  offer_message?: {
    message: any;
    serialized: string;
  };
  accept_message?: {
    message: any;
    serialized: string;
  };
  sign_message?: {
    message: any;
    serialized: string;
  };
}

interface TestResult {
  filename: string;
  messageType: string;
  success: boolean;
  error?: string;
  details?: any;
}

describe('DLC Ecosystem Compatibility Tests', () => {
  const dlcSpecsDir = path.join(__dirname, '../../test_vectors/dlcspecs');
  const rustDlcDir = path.join(__dirname, '../../test_vectors/rust-dlc');

  let dlcSpecsFiles: string[] = [];
  let rustDlcFiles: string[] = [];

  before(() => {
    // Discover all test vector files
    if (fs.existsSync(dlcSpecsDir)) {
      dlcSpecsFiles = fs
        .readdirSync(dlcSpecsDir)
        .filter((f) => f.endsWith('.json'));
    }

    if (fs.existsSync(rustDlcDir)) {
      rustDlcFiles = fs
        .readdirSync(rustDlcDir)
        .filter((f) => f.endsWith('.json'));
    }
  });

  describe('DLCSpecs Test Vector Compatibility', () => {
    it('should test all dlcspecs test vectors with version compatibility', () => {
      const allResults: TestResult[] = [];
      let oldFormatCount = 0;
      let newFormatCount = 0;

      dlcSpecsFiles.forEach((filename) => {
        const filePath = path.join(dlcSpecsDir, filename);
        let testData: DlcSpecsTestData;

        try {
          testData = JSON.parse(fs.readFileSync(filePath, 'utf8'));
        } catch (error) {
          testData = {};
        }

        // Test offer message if present
        if (testData.offer_message) {
          const result: TestResult = {
            filename,
            messageType: 'offer',
            success: false,
          };

          try {
            const inputBuffer = Buffer.from(
              testData.offer_message.serialized,
              'hex',
            );
            const expectedMessage = testData.offer_message.message;

            // Check format version based on serialization
            const messageType = inputBuffer.readUInt16BE(0);
            expect(messageType).to.equal(MessageType.DlcOffer);

            // Detect old vs new format
            const nextBytes = inputBuffer.slice(2, 7);
            const possibleProtocolVersion = nextBytes.readUInt32BE(0);
            const possibleContractFlags = nextBytes.readUInt8(4);
            const isNewFormat =
              possibleProtocolVersion >= 1 &&
              possibleProtocolVersion <= 10 &&
              possibleContractFlags === 0;

            if (isNewFormat) {
              newFormatCount++;
            } else {
              oldFormatCount++;
            }

            const offer = DlcOffer.deserialize(inputBuffer);

            expect(offer.type).to.equal(MessageType.DlcOffer);

            if (expectedMessage.protocolVersion) {
              expect(offer.protocolVersion).to.equal(
                expectedMessage.protocolVersion,
              );
            }

            if (expectedMessage.chainHash) {
              expect(offer.chainHash.toString('hex')).to.equal(
                expectedMessage.chainHash,
              );
            }

            if (expectedMessage.temporaryContractId) {
              expect(offer.temporaryContractId.toString('hex')).to.equal(
                expectedMessage.temporaryContractId,
              );
            }

            // Test round-trip serialization
            const serialized = offer.serialize();
            const deserialized = DlcOffer.deserialize(serialized);
            expect(deserialized.protocolVersion).to.equal(
              offer.protocolVersion,
            );

            result.success = true;
            result.details = {
              protocolVersion: offer.protocolVersion,
              formatVersion: isNewFormat ? 'new' : 'old',
              contractInfoType: offer.contractInfo.constructor.name,
              fundingInputsCount: offer.fundingInputs.length,
              offerCollateral: Number(offer.offerCollateral),
            };
          } catch (error) {
            result.error = error.message;
          }

          allResults.push(result);
        }

        // Test accept message if present
        if (testData.accept_message) {
          const result: TestResult = {
            filename,
            messageType: 'accept',
            success: false,
          };

          try {
            const inputBuffer = Buffer.from(
              testData.accept_message.serialized,
              'hex',
            );
            const expectedMessage = testData.accept_message.message;

            const accept = DlcAccept.deserialize(inputBuffer);

            expect(accept.type).to.equal(MessageType.DlcAccept);

            if (expectedMessage.protocolVersion) {
              expect(accept.protocolVersion).to.equal(
                expectedMessage.protocolVersion,
              );
            }

            if (expectedMessage.temporaryContractId) {
              expect(accept.temporaryContractId.toString('hex')).to.equal(
                expectedMessage.temporaryContractId,
              );
            }

            // Test round-trip
            const serialized = accept.serialize();
            const deserialized = DlcAccept.deserialize(serialized);
            expect(deserialized.protocolVersion).to.equal(
              accept.protocolVersion,
            );

            result.success = true;
            result.details = {
              protocolVersion: accept.protocolVersion,
              fundingInputsCount: accept.fundingInputs.length,
              acceptCollateral: Number(accept.acceptCollateral),
              hasNegotiationFields: !!accept.negotiationFields,
            };
          } catch (error) {
            result.error = error.message;
          }

          allResults.push(result);
        }

        // Test sign message if present
        if (testData.sign_message) {
          const result: TestResult = {
            filename,
            messageType: 'sign',
            success: false,
          };

          try {
            const inputBuffer = Buffer.from(
              testData.sign_message.serialized,
              'hex',
            );
            const expectedMessage = testData.sign_message.message;

            const sign = DlcSign.deserialize(inputBuffer);

            expect(sign.type).to.equal(MessageType.DlcSign);

            if (expectedMessage.protocolVersion) {
              expect(sign.protocolVersion).to.equal(
                expectedMessage.protocolVersion,
              );
            }

            if (expectedMessage.contractId) {
              expect(sign.contractId.toString('hex')).to.equal(
                expectedMessage.contractId,
              );
            }

            // Test round-trip
            const serialized = sign.serialize();
            const deserialized = DlcSign.deserialize(serialized);
            expect(deserialized.protocolVersion).to.equal(sign.protocolVersion);

            result.success = true;
            result.details = {
              protocolVersion: sign.protocolVersion,
              cetSignatureCount: sign.cetAdaptorSignatures.sigs.length,
              hasBatchFunding: !!sign.batchFundingGroups,
            };
          } catch (error) {
            result.error = error.message;
          }

          allResults.push(result);
        }
      });

      // Generate comprehensive report with version analysis
      const dlcSpecsSuccessRate =
        allResults.length > 0
          ? allResults.filter((r) => r.success).length / allResults.length
          : 0;

      const byMessageType = {
        offer: allResults.filter((r) => r.messageType === 'offer'),
        accept: allResults.filter((r) => r.messageType === 'accept'),
        sign: allResults.filter((r) => r.messageType === 'sign'),
      };

      const failures = allResults.filter((r) => !r.success);

      const reportLines = [
        `DLCSpecs Test Vector Results (With Version Compatibility):`,
        `Files Processed: ${dlcSpecsFiles.length}`,
        `Tests Run: ${allResults.length}`,
        `Success Rate: ${Math.round(dlcSpecsSuccessRate * 100)}% (${
          allResults.filter((r) => r.success).length
        }/${allResults.length})`,
        '',
        'Version Analysis:',
        `  • Old format (pre-protocol_version): ${oldFormatCount}`,
        `  • New format (with protocol_version): ${newFormatCount}`,
        '',
        'By Message Type:',
        ...Object.entries(byMessageType).map(([type, results]) => {
          const successCount = results.filter((r) => r.success).length;
          const rate =
            results.length > 0
              ? Math.round((successCount / results.length) * 100)
              : 0;
          return `  • ${type}: ${rate}% (${successCount}/${results.length})`;
        }),
      ];

      if (failures.length > 0) {
        reportLines.push('', 'Failures:');
        failures.forEach((f) => {
          reportLines.push(`  • ${f.filename} (${f.messageType}): ${f.error}`);
        });
      }

      // Enhanced expectation with version context
      const expectedMinRate = 0.5; // Expect reasonable compatibility with version detection
      expect(dlcSpecsSuccessRate).to.be.at.least(
        expectedMinRate,
        `DLCSpecs compatibility: ${Math.round(
          dlcSpecsSuccessRate * 100,
        )}%. Version Analysis: ${oldFormatCount} old format, ${newFormatCount} new format test vectors. \n\n${reportLines.join(
          '\n',
        )}`,
      );
    });
  });

  describe('Rust-DLC Test Vector Compatibility', () => {
    it('should test all rust-dlc test vectors comprehensively', () => {
      const allResults: TestResult[] = [];

      rustDlcFiles.forEach((filename) => {
        const filePath = path.join(rustDlcDir, filename);
        let testData: any;

        try {
          testData = JSON.parse(fs.readFileSync(filePath, 'utf8'));
        } catch (error) {
          testData = {};
        }

        const result: TestResult = {
          filename,
          messageType: 'rust-dlc-validation',
          success: false,
        };

        try {
          // Detect message type from filename or structure
          let messageType = 'unknown';
          if (filename.includes('offer')) messageType = 'offer';
          else if (filename.includes('accept')) messageType = 'accept';
          else if (filename.includes('sign')) messageType = 'sign';

          result.messageType = `rust-dlc-${messageType}`;

          // Universal rust-dlc validations
          if (testData.protocolVersion !== undefined) {
            expect(testData.protocolVersion).to.be.a('number');
            expect(testData.protocolVersion).to.equal(1);
          }

          // Message-specific validations
          if (messageType === 'offer' || testData.contractInfo) {
            expect(testData.chainHash).to.be.a('string');
            expect(testData.temporaryContractId).to.be.a('string');
            expect(testData.contractInfo).to.be.an('object');
            expect(testData.fundingPubkey).to.be.a('string');
            expect(testData.payoutSpk).to.be.a('string');

            if (testData.contractInfo.singleContractInfo) {
              expect(
                testData.contractInfo.singleContractInfo.totalCollateral,
              ).to.be.a('number');
            }
          }

          if (
            messageType === 'accept' ||
            testData.acceptCollateral !== undefined
          ) {
            expect(testData.temporaryContractId).to.be.a('string');
            expect(testData.acceptCollateral).to.be.a('number');
            expect(testData.fundingPubkey).to.be.a('string');
            expect(testData.cetAdaptorSignatures).to.be.an('object');
            expect(
              testData.cetAdaptorSignatures.ecdsaAdaptorSignatures,
            ).to.be.an('array');
          }

          if (messageType === 'sign' || testData.contractId) {
            expect(testData.contractId).to.be.a('string');
            expect(testData.cetAdaptorSignatures).to.be.an('object');
            expect(testData.refundSignature).to.be.a('string');
            expect(testData.fundingSignatures).to.be.an('object');
          }

          result.success = true;
          result.details = {
            detectedType: messageType,
            protocolVersion: testData.protocolVersion,
            hasContractInfo: !!testData.contractInfo,
            hasCetSignatures: !!testData.cetAdaptorSignatures,
          };
        } catch (error) {
          result.error = error.message;
        }

        allResults.push(result);
      });

      // Generate comprehensive report
      const rustDlcSuccessRate =
        allResults.length > 0
          ? allResults.filter((r) => r.success).length / allResults.length
          : 0;

      const failures = allResults.filter((r) => !r.success);

      const reportLines = [
        `Rust-DLC Test Vector Results:`,
        `Files Processed: ${rustDlcFiles.length}`,
        `Tests Run: ${allResults.length}`,
        `Success Rate: ${Math.round(rustDlcSuccessRate * 100)}% (${
          allResults.filter((r) => r.success).length
        }/${allResults.length})`,
      ];

      if (failures.length > 0) {
        reportLines.push('', 'Failures:');
        failures.forEach((f) => {
          reportLines.push(`  • ${f.filename}: ${f.error}`);
        });
      }

      // We expect high compatibility with rust-dlc
      expect(rustDlcSuccessRate).to.be.greaterThan(
        0.8,
        `Rust-DLC compatibility too low: ${Math.round(
          rustDlcSuccessRate * 100,
        )}%. \n\n${reportLines.join('\n')}`,
      );
    });
  });

  describe('Overall Ecosystem Compatibility Summary', () => {
    it('should provide comprehensive ecosystem compatibility metrics with version analysis', () => {
      expect(dlcSpecsFiles.length).to.be.greaterThan(
        0,
        'Should have dlcspecs test vectors',
      );
      expect(rustDlcFiles.length).to.be.greaterThan(
        0,
        'Should have rust-dlc test vectors',
      );

      // Report will be shown in console during test run through assertion messages
      expect(true).to.be.true; // Always pass, this is just for reporting
    });
  });
});
