import * as anchor from "@coral-xyz/anchor";
import { Transaction } from "@solana/web3.js";
import { expect } from "chai";
import fetch from "node-fetch";
import { getTestContext } from "../setup";

describe("Join and Leave Auction (API)", () => {
  it("Company 1 (BMW) Joins Active Auction", async () => {
    const ctx = getTestContext();
    const {
      connection,
      program,
      axumBaseUrl,
      company1Admin,
      company1Pda,
      auctionPda,
    } = ctx;

    console.log("Company 1 joining auction:", auctionPda.toBase58());

    // Step 1: Verify company is not in auction yet
    const companyBefore = await program.account.company.fetch(company1Pda);
    expect(companyBefore.inAuction).to.be.false;
    expect(companyBefore.placedBid).to.be.false;
    console.log("✓ Company 1 is not in auction yet");

    // Step 2: Verify auction is active
    const auction = await program.account.auction.fetch(auctionPda);
    expect(auction.status).to.deep.equal({ active: {} });
    expect(auction.isActive).to.be.true;
    console.log("✓ Auction is active");

    // Step 3: Create API request payload
    const request = {
      auction: auctionPda.toBase58(),
      company: company1Pda.toBase58(),
      company_admin: company1Admin.publicKey.toBase58(),
    };

    console.log("Calling API to create join auction transaction...");

    // Step 4: Call Axum API to get unsigned transaction
    const createTxResponse = await fetch(
      `${axumBaseUrl}/api/primary-market/join-auction`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(request),
      }
    );

    if (!createTxResponse.ok) {
      const error = await createTxResponse.text();
      throw new Error(`Failed to create transaction: ${error}`);
    }

    const { transaction_base64, message } = await createTxResponse.json();
    console.log(`API Response: ${message}`);

    // Step 5: Deserialize and sign transaction
    const txBuffer = Buffer.from(transaction_base64, "base64");
    const transaction = Transaction.from(txBuffer);

    console.log("Transaction signer (expected):");
    console.log("  Company 1 Admin:", company1Admin.publicKey.toBase58());

    // Sign with company1Admin
    transaction.sign(company1Admin);

    console.log("Transaction signed locally");

    // Step 6: Submit signed transaction to backend
    const signedTxBase64 = transaction.serialize().toString("base64");
    const submitTxResponse = await fetch(`${axumBaseUrl}/api/submit-tx`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ transaction_base64: signedTxBase64 }),
    });

    if (!submitTxResponse.ok) {
      const error = await submitTxResponse.text();
      throw new Error(`Failed to submit transaction: ${error}`);
    }

    const { signature } = await submitTxResponse.json();
    console.log(`✓ Transaction signature: ${signature}`);

    // Step 7: Verify on-chain state - COMPANY JOINED
    const companyAfter = await program.account.company.fetch(company1Pda);

    expect(companyAfter.inAuction).to.be.true;
    expect(companyAfter.placedBid).to.be.false;

    console.log("✓ Company 1 state verified");
    console.log(`  - In Auction: ${companyAfter.inAuction}`);
    console.log(`  - Placed Bid: ${companyAfter.placedBid}`);

    // Step 8: Verify events/logs
    const txDetails = await connection.getTransaction(signature, {
      commitment: "confirmed",
      maxSupportedTransactionVersion: 0,
    });

    if (txDetails?.meta?.logMessages) {
      const logs = txDetails.meta.logMessages;

      const hasJoinLog = logs.some((log) =>
        log.includes("Company joined auction")
      );

      if (hasJoinLog) {
        console.log("✓ Company joined auction log message found");
      }

      const hasEventLog = logs.some((log) =>
        log.includes("CompanyJoinedAuction")
      );

      if (hasEventLog) {
        console.log("✓ CompanyJoinedAuction event emitted");
      }
    }

    console.log("✓ Company 1 joined auction successfully via API");
  });

  it("Company 2 (Total) Joins Active Auction", async () => {
    const ctx = getTestContext();
    const {
      connection,
      program,
      axumBaseUrl,
      company2Admin,
      company2Pda,
      auctionPda,
    } = ctx;

    console.log("Company 2 joining auction:", auctionPda.toBase58());

    // Verify company is not in auction yet
    const companyBefore = await program.account.company.fetch(company2Pda);
    expect(companyBefore.inAuction).to.be.false;

    const request = {
      auction: auctionPda.toBase58(),
      company: company2Pda.toBase58(),
      company_admin: company2Admin.publicKey.toBase58(),
    };

    const createTxResponse = await fetch(
      `${axumBaseUrl}/api/primary-market/join-auction`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(request),
      }
    );

    if (!createTxResponse.ok) {
      const error = await createTxResponse.text();
      throw new Error(`Failed to create transaction: ${error}`);
    }

    const { transaction_base64 } = await createTxResponse.json();

    const txBuffer = Buffer.from(transaction_base64, "base64");
    const transaction = Transaction.from(txBuffer);
    transaction.sign(company2Admin);

    const signedTxBase64 = transaction.serialize().toString("base64");
    const submitTxResponse = await fetch(`${axumBaseUrl}/api/submit-tx`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ transaction_base64: signedTxBase64 }),
    });

    if (!submitTxResponse.ok) {
      const error = await submitTxResponse.text();
      throw new Error(`Failed to submit transaction: ${error}`);
    }

    const { signature } = await submitTxResponse.json();
    console.log(`✓ Transaction signature: ${signature}`);

    // Verify company joined
    const companyAfter = await program.account.company.fetch(company2Pda);
    expect(companyAfter.inAuction).to.be.true;

    console.log("✓ Company 2 joined auction successfully");
  });

  it("Should Reject Company Joining Twice", async () => {
    const ctx = getTestContext();
    const {
      axumBaseUrl,
      company1Admin,
      company1Pda,
      auctionPda,
    } = ctx;

    const request = {
      auction: auctionPda.toBase58(),
      company: company1Pda.toBase58(),
      company_admin: company1Admin.publicKey.toBase58(),
    };

    // Try to join again
    const createTxResponse = await fetch(
      `${axumBaseUrl}/api/primary-market/join-auction`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(request),
      }
    );

    expect(createTxResponse.ok).to.be.false;
    const errorText = await createTxResponse.text();
    console.log("✓ Correctly rejected duplicate join:", errorText);
  });

  it("Company 2 Leaves Auction Before Placing Bid", async () => {
    const ctx = getTestContext();
    const {
      connection,
      program,
      axumBaseUrl,
      company2Admin,
      company2Pda,
      auctionPda,
    } = ctx;

    console.log("Company 2 leaving auction...");

    // Verify company is in auction and has not placed bid
    const companyBefore = await program.account.company.fetch(company2Pda);
    expect(companyBefore.inAuction).to.be.true;
    expect(companyBefore.placedBid).to.be.false;

    const request = {
      auction: auctionPda.toBase58(),
      company: company2Pda.toBase58(),
      company_admin: company2Admin.publicKey.toBase58(),
    };

    console.log("Calling API to create leave auction transaction...");

    const createTxResponse = await fetch(
      `${axumBaseUrl}/api/primary-market/leave-auction`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(request),
      }
    );

    if (!createTxResponse.ok) {
      const error = await createTxResponse.text();
      throw new Error(`Failed to create transaction: ${error}`);
    }

    const { transaction_base64, message } = await createTxResponse.json();
    console.log(`API Response: ${message}`);

    const txBuffer = Buffer.from(transaction_base64, "base64");
    const transaction = Transaction.from(txBuffer);
    transaction.sign(company2Admin);

    const signedTxBase64 = transaction.serialize().toString("base64");
    const submitTxResponse = await fetch(`${axumBaseUrl}/api/submit-tx`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ transaction_base64: signedTxBase64 }),
    });

    if (!submitTxResponse.ok) {
      const error = await submitTxResponse.text();
      throw new Error(`Failed to submit transaction: ${error}`);
    }

    const { signature } = await submitTxResponse.json();
    console.log(`✓ Transaction signature: ${signature}`);

    // Verify company left
    const companyAfter = await program.account.company.fetch(company2Pda);
    expect(companyAfter.inAuction).to.be.false;

    console.log("✓ Company 2 left auction successfully");

    // Verify events/logs
    const txDetails = await connection.getTransaction(signature, {
      commitment: "confirmed",
      maxSupportedTransactionVersion: 0,
    });

    if (txDetails?.meta?.logMessages) {
      const logs = txDetails.meta.logMessages;

      const hasLeaveLog = logs.some((log) =>
        log.includes("Company left auction")
      );

      if (hasLeaveLog) {
        console.log("✓ Company left auction log message found");
      }
    }
  });

  it("Company 2 Rejoins Auction", async () => {
    const ctx = getTestContext();
    const {
      program,
      axumBaseUrl,
      company2Admin,
      company2Pda,
      auctionPda,
    } = ctx;

    // Rejoin for subsequent tests
    const request = {
      auction: auctionPda.toBase58(),
      company: company2Pda.toBase58(),
      company_admin: company2Admin.publicKey.toBase58(),
    };

    const createTxResponse = await fetch(
      `${axumBaseUrl}/api/primary-market/join-auction`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(request),
      }
    );

    if (!createTxResponse.ok) {
      const error = await createTxResponse.text();
      throw new Error(`Failed to create transaction: ${error}`);
    }

    const { transaction_base64 } = await createTxResponse.json();

    const txBuffer = Buffer.from(transaction_base64, "base64");
    const transaction = Transaction.from(txBuffer);
    transaction.sign(company2Admin);

    const signedTxBase64 = transaction.serialize().toString("base64");
    const submitTxResponse = await fetch(`${axumBaseUrl}/api/submit-tx`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ transaction_base64: signedTxBase64 }),
    });

    if (!submitTxResponse.ok) {
      const error = await submitTxResponse.text();
      throw new Error(`Failed to submit transaction: ${error}`);
    }

    const { signature } = await submitTxResponse.json();
    console.log(`✓ Company 2 rejoined: ${signature}`);

    // Verify
    const companyAfter = await program.account.company.fetch(company2Pda);
    expect(companyAfter.inAuction).to.be.true;
  });
});
