<?php

// PetitionFormTest.php (loads first) already required petition-form.php and
// defined the Context stub. This file only adds CSV-function tests.

const PETITION_CSV_HEADER = [
    'timestamp', 'email', 'firstname', 'surname',
    'region', 'display', 'status', 'token', 'token_created', 'ip_hash',
];

/**
 * Write rows to a temp CSV and return its path. Caller must unlink().
 */
function petitionTestCsv(array $rows): string
{
    $path = tempnam(sys_get_temp_dir(), 'petition_form_test_');
    $fp = fopen($path, 'w');
    foreach ($rows as $row) {
        fputcsv($fp, $row, ',', '"', '');
    }
    fclose($fp);
    return $path;
}

/**
 * Build a minimal signature data array for petitionAppendSignature.
 */
function sigData(string $email, string $status = 'pending', string $token = 'tok'): array
{
    return [
        'timestamp'     => time(),
        'email'         => $email,
        'firstname'     => 'Test',
        'surname'       => 'User',
        'region'        => 'oslo',
        'display'       => 'semi',
        'status'        => $status,
        'token'         => $token,
        'token_created' => time(),
        'ip_hash'       => 'hash',
    ];
}

// --- petitionEmailExists ---

it('returns false when the CSV does not exist', function () {
    expect(petitionEmailExists('/tmp/nonexistent-petition-form-test.csv', 'a@b.com'))->toBeFalse();
});

it('returns false when the email is not in the CSV', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'other@b.com', 'Ola', 'Hansen', 'oslo', 'semi', 'confirmed', 't1', time(), 'h1'],
    ]);
    expect(petitionEmailExists($path, 'nothere@b.com'))->toBeFalse();
    unlink($path);
});

it('returns true when the email exists', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'kari@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'confirmed', 't1', time(), 'h1'],
    ]);
    expect(petitionEmailExists($path, 'kari@b.com'))->toBeTrue();
    unlink($path);
});

it('matches the email case-insensitively', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'kari@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'confirmed', 't1', time(), 'h1'],
    ]);
    expect(petitionEmailExists($path, 'KARI@B.COM'))->toBeTrue();
    unlink($path);
});

// --- petitionGetConfirmedSignatures ---

it('returns an empty array when the CSV does not exist', function () {
    expect(petitionGetConfirmedSignatures('/tmp/nonexistent-petition-form-test2.csv'))->toBe([]);
});

it('returns only confirmed signatures', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'a@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'confirmed', 't1', time(), 'h1'],
        [time(), 'b@b.com', 'Ola',  'Nilsen', 'oslo', 'semi', 'pending',   't2', time(), 'h2'],
        [time(), 'c@b.com', 'Per',  'Olsen',  'oslo', 'semi', 'deleted',   't3', time(), 'h3'],
    ]);
    $sigs = petitionGetConfirmedSignatures($path);
    expect(count($sigs))->toBe(1);
    expect($sigs[0]['firstname'])->toBe('Kari');
    unlink($path);
});

it('sorts confirmed signatures newest-first', function () {
    $older = time() - 100;
    $newer = time();
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [$older, 'a@b.com', 'Older', 'Hansen', 'oslo', 'semi', 'confirmed', 't1', $older, 'h1'],
        [$newer, 'b@b.com', 'Newer', 'Nilsen', 'oslo', 'semi', 'confirmed', 't2', $newer, 'h2'],
    ]);
    $sigs = petitionGetConfirmedSignatures($path);
    expect($sigs[0]['firstname'])->toBe('Newer');
    expect($sigs[1]['firstname'])->toBe('Older');
    unlink($path);
});

it('does not include email or token in the returned signature data', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'secret@b.com', 'Lars', 'Berg', 'oslo', 'semi', 'confirmed', 'secret-tok', time(), 'h1'],
    ]);
    $sig = petitionGetConfirmedSignatures($path)[0];
    expect($sig)->not->toHaveKey('email');
    expect($sig)->not->toHaveKey('token');
    unlink($path);
});

// --- petitionGetSignatureByToken ---

it('returns null when the CSV does not exist', function () {
    expect(petitionGetSignatureByToken('/tmp/nonexistent.csv', 'tok'))->toBeNull();
});

it('returns null for an unknown token', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'a@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'pending', 'real-tok', time(), 'h1'],
    ]);
    expect(petitionGetSignatureByToken($path, 'wrong-tok'))->toBeNull();
    unlink($path);
});

it('returns email, firstname, and surname for a matching token', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'kari@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'pending', 'my-tok', time(), 'h1'],
    ]);
    $sig = petitionGetSignatureByToken($path, 'my-tok');
    expect($sig)->not->toBeNull();
    expect($sig['email'])->toBe('kari@b.com');
    expect($sig['firstname'])->toBe('Kari');
    expect($sig['surname'])->toBe('Hansen');
    unlink($path);
});

// --- petitionGetPendingSignatureByEmail ---

it('returns null when the CSV does not exist for pending lookup', function () {
    expect(petitionGetPendingSignatureByEmail('/tmp/nonexistent.csv', 'a@b.com'))->toBeNull();
});

it('returns null when the email is not present', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'other@b.com', 'Ola', 'Hansen', 'oslo', 'semi', 'pending', 't1', time(), 'h1'],
    ]);
    expect(petitionGetPendingSignatureByEmail($path, 'missing@b.com'))->toBeNull();
    unlink($path);
});

it('returns the signature when the email matches', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'kari@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'pending', 'tok1', time(), 'h1'],
    ]);
    $sig = petitionGetPendingSignatureByEmail($path, 'kari@b.com');
    expect($sig)->not->toBeNull();
    expect($sig['email'])->toBe('kari@b.com');
    expect($sig['status'])->toBe('pending');
    unlink($path);
});

it('matches the email case-insensitively for pending lookup', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'kari@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'pending', 'tok1', time(), 'h1'],
    ]);
    expect(petitionGetPendingSignatureByEmail($path, 'KARI@B.COM'))->not->toBeNull();
    unlink($path);
});

// --- petitionAppendSignature ---

it('creates a new CSV with a header and appends the first signature', function () {
    $path = tempnam(sys_get_temp_dir(), 'petition_append_test_');
    unlink($path); // petitionAppendSignature creates it

    $result = petitionAppendSignature($path, sigData('new@b.com'));
    expect($result)->toBeTrue();
    expect(file_exists($path))->toBeTrue();

    $rows = array_filter(explode("\n", file_get_contents($path)));
    expect(count($rows))->toBe(2); // header + 1 row
    unlink($path);
});

it('appends a signature to an existing CSV', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'first@b.com', 'First', 'User', 'oslo', 'semi', 'pending', 't1', time(), 'h1'],
    ]);
    petitionAppendSignature($path, sigData('second@b.com', 'pending', 't2'));

    $fp = fopen($path, 'r');
    fgetcsv($fp, null, ',', '"', ''); // skip header
    $rows = [];
    while (($row = fgetcsv($fp, null, ',', '"', '')) !== false) $rows[] = $row;
    fclose($path);

    expect(count($rows))->toBe(2);
    unlink($path);
});

it('rejects a duplicate email and returns false', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'existing@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'pending', 't1', time(), 'h1'],
    ]);
    $result = petitionAppendSignature($path, sigData('existing@b.com', 'pending', 't2'));
    expect($result)->toBeFalse();
    unlink($path);
});

it('rejects a duplicate email case-insensitively', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'kari@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'pending', 't1', time(), 'h1'],
    ]);
    $result = petitionAppendSignature($path, sigData('KARI@B.COM', 'pending', 't2'));
    expect($result)->toBeFalse();
    unlink($path);
});

// --- petitionConfirmSignature ---

it('returns error when the CSV does not exist', function () {
    expect(petitionConfirmSignature('/tmp/nonexistent.csv', 'tok'))->toBe('error');
});

it('returns error for an unknown token', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'a@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'pending', 'real-tok', time(), 'h1'],
    ]);
    expect(petitionConfirmSignature($path, 'wrong-tok'))->toBe('error');
    unlink($path);
});

it('returns success and sets status to confirmed for a valid pending token', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'a@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'pending', 'valid-tok', time(), 'h1'],
    ]);
    expect(petitionConfirmSignature($path, 'valid-tok'))->toBe('success');

    // Verify the status was actually updated in the file
    $fp = fopen($path, 'r');
    fgetcsv($fp, null, ',', '"', ''); // header
    $row = fgetcsv($fp, null, ',', '"', '');
    fclose($fp);
    expect($row[6])->toBe('confirmed');
    unlink($path);
});

it('returns already when the token is already confirmed', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'a@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'confirmed', 'valid-tok', time(), 'h1'],
    ]);
    expect(petitionConfirmSignature($path, 'valid-tok'))->toBe('already');
    unlink($path);
});

it('returns expired when the token is older than 30 days', function () {
    $oldTime = time() - (31 * 86400);
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [$oldTime, 'a@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'pending', 'old-tok', $oldTime, 'h1'],
    ]);
    expect(petitionConfirmSignature($path, 'old-tok'))->toBe('expired');
    unlink($path);
});

// --- petitionDeleteSignature ---

it('returns error when the CSV does not exist for delete', function () {
    expect(petitionDeleteSignature('/tmp/nonexistent.csv', 'tok'))->toBe('error');
});

it('returns error when the token is not found for delete', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'a@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'confirmed', 'real-tok', time(), 'h1'],
    ]);
    expect(petitionDeleteSignature($path, 'wrong-tok'))->toBe('error');
    unlink($path);
});

it('returns success and removes the row for a matching token', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'a@b.com', 'Kari',  'Hansen', 'oslo', 'semi', 'confirmed', 'del-tok',  time(), 'h1'],
        [time(), 'b@b.com', 'Other', 'Nilsen', 'oslo', 'semi', 'confirmed', 'keep-tok', time(), 'h2'],
    ]);
    expect(petitionDeleteSignature($path, 'del-tok'))->toBe('success');

    $fp = fopen($path, 'r');
    fgetcsv($fp, null, ',', '"', ''); // header
    $rows = [];
    while (($row = fgetcsv($fp, null, ',', '"', '')) !== false) $rows[] = $row;
    fclose($fp);

    expect(count($rows))->toBe(1);
    expect($rows[0][7])->toBe('keep-tok');
    unlink($path);
});

// --- petitionUpdateSignatureToken ---

it('returns false when the CSV does not exist for token update', function () {
    expect(petitionUpdateSignatureToken('/tmp/nonexistent.csv', 'a@b.com', 'new-tok'))->toBeFalse();
});

it('returns false when the email has no pending entry', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'a@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'confirmed', 'old-tok', time(), 'h1'],
    ]);
    expect(petitionUpdateSignatureToken($path, 'a@b.com', 'new-tok'))->toBeFalse();
    unlink($path);
});

it('returns true and updates the token for a pending entry', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'a@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'pending', 'old-tok', time(), 'h1'],
    ]);
    expect(petitionUpdateSignatureToken($path, 'a@b.com', 'new-tok'))->toBeTrue();

    $fp = fopen($path, 'r');
    fgetcsv($fp, null, ',', '"', ''); // header
    $row = fgetcsv($fp, null, ',', '"', '');
    fclose($fp);
    expect($row[7])->toBe('new-tok');
    unlink($path);
});

it('only updates pending entries, not confirmed ones', function () {
    $path = petitionTestCsv([
        PETITION_CSV_HEADER,
        [time(), 'a@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'confirmed', 'conf-tok', time(), 'h1'],
    ]);
    $result = petitionUpdateSignatureToken($path, 'a@b.com', 'new-tok');
    expect($result)->toBeFalse();

    // Confirmed token should be unchanged
    $fp = fopen($path, 'r');
    fgetcsv($fp, null, ',', '"', '');
    $row = fgetcsv($fp, null, ',', '"', '');
    fclose($fp);
    expect($row[7])->toBe('conf-tok');
    unlink($path);
});
