/**
 * @file: perwerde.live.test.js
 * @description: Live integration tests for the Perwerde (Education Platform) pallet.
 * 
 * @preconditions:
 *  1. A local Pezkuwi dev node must be running and accessible at `ws://127.0.0.1:8082`.
 *  2. The node must have the `perwerde` pallet included.
 */

import { ApiPromise, WsProvider, Keyring } from '@pezkuwi/api';

// ========================================
// TEST CONFIGURATION
// ========================================

const WS_ENDPOINT = 'ws://127.0.0.1:8082';
jest.setTimeout(60000); // 60 seconds

// ========================================
// TEST SETUP & TEARDOWN
// ========================================

let api;
let keyring;
let admin, student1, nonAdmin;
let courseId = 0;

beforeAll(async () => {
  const wsProvider = new WsProvider(WS_ENDPOINT);
  api = await ApiPromise.create({ provider: wsProvider });
  keyring = new Keyring({ type: 'sr25519' });
  
  // Per mock.rs, admin is account 0, which is //Alice
  admin = keyring.addFromUri('//Alice');
  student1 = keyring.addFromUri('//Charlie');
  nonAdmin = keyring.addFromUri('//Dave');

  console.log('Connected to node for Perwerde tests.');
});

afterAll(async () => {
  if (api) await api.disconnect();
  console.log('Disconnected from node.');
});

// Helper to wait for the next finalized block and get the tx result
const sendAndFinalize = async (tx) => {
    return new Promise((resolve, reject) => {
        tx.signAndSend(admin, ({ status, dispatchError, events }) => {
            if (status.isFinalized) {
                if (dispatchError) {
                    const decoded = api.registry.findMetaError(dispatchError.asModule);
                    const errorMsg = `${decoded.section}.${decoded.name}`;
                    reject(new Error(errorMsg));
                } else {
                    resolve(events);
                }
            }
        }).catch(reject);
    });
};

// ========================================
// LIVE PALLET TESTS (Translated from .rs)
// ========================================

describe('Perwerde Pallet Live Tests', () => {

  /**
   * Corresponds to: `create_course_works` and `next_course_id_increments_correctly`
   */
  it('should allow an admin to create a course', async () => {
    const nextCourseId = await api.query.perwerde.nextCourseId();
    courseId = nextCourseId.toNumber();

    const tx = api.tx.perwerde.createCourse(
      "Blockchain 101",
      "An introduction to blockchain technology.",
      "https://example.com/blockchain101"
    );
    await sendAndFinalize(tx);

    const course = (await api.query.perwerde.courses(courseId)).unwrap();
    expect(course.owner.toString()).toBe(admin.address);
    expect(course.name.toHuman()).toBe("Blockchain 101");
  });

  /**
   * Corresponds to: `create_course_fails_for_non_admin`
   */
  it('should NOT allow a non-admin to create a course', async () => {
    const tx = api.tx.perwerde.createCourse(
      "Unauthorized Course", "Desc", "URL"
    );
    
    // We expect this transaction to fail with a BadOrigin error
    await expect(
        sendAndFinalize(tx.sign(nonAdmin)) // Sign with the wrong account
    ).rejects.toThrow('system.BadOrigin');
  });

  /**
   * Corresponds to: `enroll_works` and part of `complete_course_works`
   */
  it('should allow a student to enroll in and complete a course', async () => {
    // Phase 1: Enroll
    const enrollTx = api.tx.perwerde.enroll(courseId);
    await sendAndFinalize(enrollTx.sign(student1));

    let enrollment = (await api.query.perwerde.enrollments([student1.address, courseId])).unwrap();
    expect(enrollment.student.toString()).toBe(student1.address);
    expect(enrollment.completedAt.isNone).toBe(true);

    // Phase 2: Complete
    const points = 95;
    const completeTx = api.tx.perwerde.completeCourse(courseId, points);
    await sendAndFinalize(completeTx.sign(student1));
    
    enrollment = (await api.query.perwerde.enrollments([student1.address, courseId])).unwrap();
    expect(enrollment.completedAt.isSome).toBe(true);
    expect(enrollment.pointsEarned.toNumber()).toBe(points);
  });
  
  /**
   * Corresponds to: `enroll_fails_if_already_enrolled`
   */
  it('should fail if a student tries to enroll in the same course twice', async () => {
    // Student1 is already enrolled from the previous test.
    const enrollTx = api.tx.perwerde.enroll(courseId);

    await expect(
        sendAndFinalize(enrollTx.sign(student1))
    ).rejects.toThrow('perwerde.AlreadyEnrolled');
  });
  
  /**
   * Corresponds to: `archive_course_works`
   */
  it('should allow the course owner to archive it', async () => {
    const archiveTx = api.tx.perwerde.archiveCourse(courseId);
    await sendAndFinalize(archiveTx); // Signed by admin by default in helper

    const course = (await api.query.perwerde.courses(courseId)).unwrap();
    expect(course.status.toString()).toBe('Archived');
  });

  /**
   * Corresponds to: `enroll_fails_for_archived_course`
   */
  it('should fail if a student tries to enroll in an archived course', async () => {
    const newStudent = keyring.addFromUri('//Ferdie');
    const enrollTx = api.tx.perwerde.enroll(courseId);

    await expect(
        sendAndFinalize(enrollTx.sign(newStudent))
    ).rejects.toThrow('perwerde.CourseNotActive');
  });
});
