<?php

require_once __DIR__ . '/db.php';

const ROLE_HEAD = 'head_of_household';
const ROLE_ADULT = 'adult';
const ROLE_CHILD = 'child';

const SESSION_ACTIVE_PERSON = 'active_person_id';
const SESSION_HOH_VERIFIED = 'hoh_verified';

function startFamilyHubSession(): void {
    if (session_status() === PHP_SESSION_NONE) {
        session_start();
    }
}

function getActivePersonId(): ?string {
    $id = $_SESSION[SESSION_ACTIVE_PERSON] ?? null;
    return is_string($id) && $id !== '' ? $id : null;
}

function setSessionPerson(string $personId, bool $hohVerified): void {
    $_SESSION[SESSION_ACTIVE_PERSON] = $personId;
    $_SESSION[SESSION_HOH_VERIFIED] = $hohVerified;
}

function clearPersonaSession(): void {
    unset($_SESSION[SESSION_ACTIVE_PERSON], $_SESSION[SESSION_HOH_VERIFIED]);
}

function isHohVerified(): bool {
    return !empty($_SESSION[SESSION_HOH_VERIFIED]);
}

function findPersonById(array $people, string $id): ?array {
    foreach ($people as $p) {
        if (($p['id'] ?? '') === $id) {
            return $p;
        }
    }
    return null;
}

function getActivePerson(array $people): ?array {
    $id = getActivePersonId();
    if ($id === null) {
        return null;
    }
    return findPersonById($people, $id);
}

function personRequiresPinToActivate(?array $person): bool {
    if ($person === null) {
        return false;
    }
    return ($person['role'] ?? '') === ROLE_HEAD && !empty($person['pin_hash']);
}

/**
 * True when at least one person is recorded as Head of household (setup complete).
 */
function familyHubHasHeadOfHousehold(array $people): bool {
    foreach ($people as $p) {
        if (($p['role'] ?? '') === ROLE_HEAD) {
            return true;
        }
    }
    return false;
}

function assertHoHCanManagePeople(array $people): void {
    if (count($people) === 0) {
        return;
    }
    $active = getActivePerson($people);
    if ($active === null || ($active['role'] ?? '') !== ROLE_HEAD || !isHohVerified()) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Head of household verification required.']);
        exit;
    }
}

/**
 * @param mixed $raw
 * @return array<int, array<string, mixed>>
 */
function normalizePeopleList($raw): array {
    if (!is_array($raw) || !array_is_list($raw)) {
        return [];
    }
    $out = [];
    foreach ($raw as $row) {
        if (is_array($row) && !empty($row['id']) && is_string($row['id'])) {
            $out[] = $row;
        }
    }
    return $out;
}

/**
 * @param array<string, mixed> $person
 * @return array<string, mixed>
 */
function migrateLegacyPersonRow(array $person): array {
    $currencyBalance = is_numeric($person['currency_balance'] ?? null) ? (float) $person['currency_balance'] : 0.0;
    $checkingBalance = array_key_exists('checking_balance', $person) && is_numeric($person['checking_balance'])
        ? (float) $person['checking_balance']
        : $currencyBalance;
    $person['checking_balance'] = round($checkingBalance, 2);
    if (!array_key_exists('currency_balance', $person) || !is_numeric($person['currency_balance'])) {
        $person['currency_balance'] = $person['checking_balance'];
    } else {
        $person['currency_balance'] = round((float) $person['currency_balance'], 2);
    }
    if (!array_key_exists('savings_balance', $person) || !is_numeric($person['savings_balance'])) {
        $person['savings_balance'] = 0.0;
    } else {
        $person['savings_balance'] = round((float) $person['savings_balance'], 2);
    }
    if (!array_key_exists('charity_pending_balance', $person) || !is_numeric($person['charity_pending_balance'])) {
        $person['charity_pending_balance'] = 0.0;
    } else {
        $person['charity_pending_balance'] = round((float) $person['charity_pending_balance'], 2);
    }
    if (!array_key_exists('charity_donated_total', $person) || !is_numeric($person['charity_donated_total'])) {
        $person['charity_donated_total'] = 0.0;
    } else {
        $person['charity_donated_total'] = round((float) $person['charity_donated_total'], 2);
    }
    if (!array_key_exists('donation_goal_monthly', $person) || !is_numeric($person['donation_goal_monthly'])) {
        $person['donation_goal_monthly'] = 0.0;
    } else {
        $person['donation_goal_monthly'] = max(0, round((float) $person['donation_goal_monthly'], 2));
    }
    if (!array_key_exists('banking_interest_last_applied_at', $person) || !is_string($person['banking_interest_last_applied_at'])) {
        $person['banking_interest_last_applied_at'] = '';
    }
    if (!array_key_exists('nfc_submit_token_hash', $person) || !is_string($person['nfc_submit_token_hash'])) {
        $person['nfc_submit_token_hash'] = '';
    }
    if (!array_key_exists('nfc_submit_token_updated_at', $person) || !is_string($person['nfc_submit_token_updated_at'])) {
        $person['nfc_submit_token_updated_at'] = '';
    }
    return $person;
}

/**
 * @param array<int, array<string, mixed>> $people
 * @return array<int, array<string, mixed>>
 */
function migrateAllPeople(array $people): array {
    $out = [];
    foreach ($people as $person) {
        if (!is_array($person)) {
            continue;
        }
        $out[] = migrateLegacyPersonRow($person);
    }
    return $out;
}

/**
 * @param array<int, array<string, mixed>> $people
 * @return array{index:int, person:array<string, mixed>}|null
 */
function findPersonBySubmitToken(array $people, string $token): ?array {
    $token = trim($token);
    if ($token === '') {
        return null;
    }
    $candidateHash = hash('sha256', $token);
    foreach ($people as $index => $person) {
        $storedHash = trim((string) ($person['nfc_submit_token_hash'] ?? ''));
        if ($storedHash === '') {
            continue;
        }
        if (hash_equals($storedHash, $candidateHash)) {
            return ['index' => (int) $index, 'person' => $person];
        }
    }
    return null;
}
