<?php
namespace Core;

/**
 * Resolves what an organization should pay to activate its subscription:
 * which plan, in which currency, for how much.
 *
 * Currency/amount comes from the admin-managed plan_currency_prices table
 * (Admin > Subscription Plans), keyed by the currency derived from the
 * visitor's IP-resolved country (see GeoIP). Falls back to the platform's
 * default currency, and finally to the plan's own base price, so a payment
 * is never blocked purely by missing per-currency configuration.
 */
class Pricing {
    /** Returns null only if there is no active plan at all to charge for. */
    public static function resolveForOrg(int $orgId): ?array {
        $db = \Database::getInstance();

        $org = self::fetchOrg($db, $orgId);
        if (!$org) return null;

        $planId = $org['plan_id'] ? (int)$org['plan_id'] : self::defaultPlanId($db);
        if (!$planId) return null;

        $plan = self::fetchPlan($db, $planId);
        if (!$plan) return null;

        $currency = $org['billing_currency'] ?: null;
        $country  = $org['billing_country'] ?: null;

        if (!$currency) {
            $geo      = GeoIP::lookup(GeoIP::clientIp());
            $country  = $geo['country'] ?? null;
            $currency = $geo['currency'] ?? null;

            // Cache the result (even if null) so we don't re-hit the geolocation
            // API on every page load — pricing then stays stable for this org.
            $stmt = $db->prepare("UPDATE organizations SET billing_country = ?, billing_currency = ? WHERE id = ?");
            $stmt->execute([$country, $currency, $orgId]);
        }

        $defaultCurrency = defined('DEFAULT_CURRENCY') ? DEFAULT_CURRENCY : 'USD';

        $resolvedCurrency = null;
        $amount = null;
        if ($currency) {
            $amount = self::priceFor($db, $planId, $currency);
            if ($amount !== null) $resolvedCurrency = $currency;
        }
        if ($amount === null) {
            $amount = self::priceFor($db, $planId, $defaultCurrency);
            if ($amount !== null) $resolvedCurrency = $defaultCurrency;
        }
        if ($amount === null) {
            $amount = (float) $plan['price'];
            $resolvedCurrency = $defaultCurrency;
        }

        return [
            'plan'     => $plan,
            'country'  => $country,
            'currency' => $resolvedCurrency,
            'amount'   => (float) $amount,
            'symbol'   => CURRENCIES[$resolvedCurrency] ?? $resolvedCurrency,
        ];
    }

    /**
     * Public pricing for the marketing site — every purchasable (active,
     * price > 0) plan, resolved to a single consistent currency for a
     * visitor who has no organization yet.
     *
     * Currency comes from, in order: an explicit override (the pricing
     * page's manual "switch currency" control), a session-cached prior
     * resolution, or a fresh IP lookup — cached to session so repeat visits
     * don't re-hit the geolocation API. If the resolved currency isn't
     * priced for every plan, the whole page falls back to the platform
     * default currency instead, rather than showing a mix of currencies
     * across cards.
     */
    public static function resolvePublicPlans(?string $forceCurrency = null): array {
        $db = \Database::getInstance();
        $defaultCurrency = defined('DEFAULT_CURRENCY') ? DEFAULT_CURRENCY : 'USD';

        $availableCurrencies = array_column(
            $db->query("SELECT DISTINCT currency_code FROM plan_currency_prices ORDER BY currency_code ASC")->fetchAll(),
            'currency_code'
        );
        if (!in_array($defaultCurrency, $availableCurrencies, true)) {
            $availableCurrencies[] = $defaultCurrency;
            sort($availableCurrencies);
        }

        $country = $_SESSION['public_billing_country'] ?? null;

        if ($forceCurrency && in_array($forceCurrency, $availableCurrencies, true)) {
            $candidate = $forceCurrency;
        } elseif (!empty($_SESSION['public_billing_currency'])) {
            $candidate = $_SESSION['public_billing_currency'];
        } else {
            $geo = GeoIP::lookup(GeoIP::clientIp());
            $country = $geo['country'] ?? null;
            $candidate = ($geo['currency'] ?? null) ?: $defaultCurrency;
        }

        $plans = $db->query("SELECT * FROM plans WHERE is_active = 1 AND price > 0 ORDER BY price ASC")->fetchAll();

        $currency = (in_array($candidate, $availableCurrencies, true) && self::allPlansPricedIn($db, $plans, $candidate))
            ? $candidate
            : $defaultCurrency;

        $_SESSION['public_billing_country']  = $country;
        $_SESSION['public_billing_currency'] = $currency;

        $resolved = [];
        foreach ($plans as $plan) {
            $amount = self::priceFor($db, (int)$plan['id'], $currency) ?? (float)$plan['price'];
            $resolved[] = [
                'plan'   => $plan,
                'amount' => (float)$amount,
            ];
        }

        return [
            'country'             => $country,
            'currency'            => $currency,
            'symbol'              => CURRENCIES[$currency] ?? $currency,
            'availableCurrencies' => $availableCurrencies,
            'plans'               => $resolved,
        ];
    }

    private static function allPlansPricedIn(\PDO $db, array $plans, string $currency): bool {
        if (empty($plans)) return false;
        foreach ($plans as $plan) {
            if (self::priceFor($db, (int)$plan['id'], $currency) === null) return false;
        }
        return true;
    }

    /**
     * The plan a trial rolls into by default: the cheapest active plan that
     * actually costs something. Free/$0 plans (e.g. a legacy "Trial" plan
     * row distinct from the trial *status* mechanism) are deliberately
     * excluded — falling back to one would let an expired trial "pay" $0
     * and bypass the paywall entirely.
     */
    public static function defaultPlanId(\PDO $db): ?int {
        $stmt = $db->query("SELECT id FROM plans WHERE is_active = 1 AND price > 0 ORDER BY price ASC LIMIT 1");
        $id = $stmt->fetchColumn();
        return $id ? (int)$id : null;
    }

    private static function fetchOrg(\PDO $db, int $orgId): ?array {
        $stmt = $db->prepare("SELECT id, plan_id, billing_country, billing_currency FROM organizations WHERE id = ?");
        $stmt->execute([$orgId]);
        $row = $stmt->fetch();
        return $row ?: null;
    }

    private static function fetchPlan(\PDO $db, int $planId): ?array {
        $stmt = $db->prepare("SELECT * FROM plans WHERE id = ? AND is_active = 1");
        $stmt->execute([$planId]);
        $row = $stmt->fetch();
        return $row ?: null;
    }

    private static function priceFor(\PDO $db, int $planId, string $currency): ?float {
        $stmt = $db->prepare("SELECT amount FROM plan_currency_prices WHERE plan_id = ? AND currency_code = ?");
        $stmt->execute([$planId, $currency]);
        $amount = $stmt->fetchColumn();
        return $amount !== false ? (float)$amount : null;
    }
}
