<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

/**
 * What it takes to charge the figure the website shows.
 *
 * The catalogue was pushed to Stripe at its NET amount and no tax was configured
 * on the checkout, so a customer who was quoted 214,80 € paid 179,00 € — and
 * then received an invoice stating 179,00 € net plus 35,80 € VAT. Under § 11
 * Abs. 12 UStG the VAT written on an invoice is owed to the tax office whether
 * or not it was collected, so every document issued created a liability against
 * money that never contained it.
 *
 * The fix is that the Stripe Price carries the GROSS figure. `plan_prices.amount_cents`
 * and `subscription_addons.price_cents` stay NET — they are frozen onto contracts
 * and PlanChange prorates against them, and reinterpreting them would corrupt
 * every contract and every pro-rata sum ever computed. What changes is only the
 * amount Stripe is asked to take.
 *
 * A Stripe Price is immutable, so that is a NEW Price per priced row, the old one
 * archived, and every live subscription moved onto it. Both tables below exist so
 * that can be done idempotently and so a contract billing on a superseded Price
 * can still be told what it is actually paying.
 */
return new class extends Migration
{
    public function up(): void
    {
        /**
         * Every Stripe Price a priced catalogue row has ever had.
         *
         * `plan_prices.stripe_price_id` still points at the one that is on sale —
         * the checkout reads it there and must keep working — and this is the
         * full history behind it. Two reasons it is a table rather than a second
         * column:
         *
         *  - a contract opened last year is still billed on the Price it was
         *    opened with, and the invoice we issue for that cycle has to be able
         *    to NAME the line. Overwriting the id in place would leave those
         *    lines anonymous;
         *  - the sync has to be able to answer "does a Price for this figure
         *    already exist" without asking Stripe, or a second run mints a
         *    duplicate that nothing can tell apart afterwards.
         */
        Schema::create('stripe_plan_prices', function (Blueprint $table) {
            $table->id();
            $table->foreignId('plan_price_id')->constrained()->cascadeOnDelete();
            // What the Price charges — GROSS. Deliberately not called
            // `amount_cents` like the catalogue column beside it: the two are
            // different numbers now, and a shared name is how they get confused.
            $table->unsignedInteger('charged_cents');
            $table->string('stripe_price_id')->unique();
            // Set when the Price has been superseded and archived in Stripe.
            // The row stays for ever: a contract may still be billing on it.
            $table->timestamp('archived_at')->nullable();
            $table->timestamps();

            // One Price per row per figure. A second would leave two live Stripe
            // Prices for one plan at one amount and no way to tell which a
            // customer is on.
            $table->unique(['plan_price_id', 'charged_cents'], 'stripe_plan_prices_unique');
            $table->index(['plan_price_id', 'archived_at']);
        });

        /*
         * The Prices that already exist charge the NET figure, because that is
         * what they were created from. Recorded as such rather than left
         * unexplained, so the sync can see they are the wrong amount and replace
         * them — and so a cycle invoice for a contract still on one of them can
         * still name its line.
         */
        $rows = DB::table('plan_prices')
            ->whereNotNull('stripe_price_id')
            ->select('id', 'amount_cents', 'stripe_price_id')
            ->get();

        foreach ($rows as $row) {
            DB::table('stripe_plan_prices')->insert([
                'plan_price_id' => $row->id,
                'charged_cents' => $row->amount_cents,
                'stripe_price_id' => $row->stripe_price_id,
                'archived_at' => null,
                'created_at' => now(),
                'updated_at' => now(),
            ]);
        }

        Schema::table('stripe_addon_prices', function (Blueprint $table) {
            // The catalogue figure this Price was formed from. `amount_cents`
            // has always meant "what the Price charges" and still does — it is
            // the gross now — so the net it came from needs somewhere of its
            // own, or nothing could tell a repriced module from a differently
            // priced one.
            $table->unsignedInteger('net_cents')->nullable()->after('amount_cents');
            $table->timestamp('archived_at')->nullable()->after('stripe_price_id');

            $table->index(['addon_key', 'net_cents', 'currency', 'interval'], 'stripe_addon_prices_net');
        });

        // Same story as the plan side: what is there charges the net figure.
        DB::table('stripe_addon_prices')->update(['net_cents' => DB::raw('amount_cents')]);
    }

    public function down(): void
    {
        Schema::table('stripe_addon_prices', function (Blueprint $table) {
            $table->dropIndex('stripe_addon_prices_net');
            $table->dropColumn(['net_cents', 'archived_at']);
        });

        Schema::dropIfExists('stripe_plan_prices');
    }
};
