<?php

use App\Models\Plan;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

beforeEach(function () {
    $this->withHeaders(['X-Api-Key' => config('app.api_secret_key')]);
});

it('registers a new user and returns a token', function () {
    $this->postJson('/api/auth/register', [
        'name' => 'Test User',
        'email' => 'test@example.com',
        'password' => 'password',
        'password_confirmation' => 'password',
    ])
        ->assertCreated()
        ->assertJsonStructure(['token', 'user' => ['id', 'name', 'email']]);
});

it('returns 422 when register fields are missing', function () {
    $this->postJson('/api/auth/register')
        ->assertUnprocessable()
        ->assertJsonValidationErrors(['name', 'email', 'password']);
});

it('returns 422 when email is already taken', function () {
    User::factory()->create(['email' => 'taken@example.com']);

    $this->postJson('/api/auth/register', [
        'name' => 'Another User',
        'email' => 'taken@example.com',
        'password' => 'password',
        'password_confirmation' => 'password',
    ])
        ->assertUnprocessable()
        ->assertJsonValidationErrors(['email']);
});

it('logs in with valid credentials and returns a token', function () {
    $user = User::factory()->create(['password' => bcrypt('secret123')]);

    $this->postJson('/api/auth/login', [
        'email' => $user->email,
        'password' => 'secret123',
    ])
        ->assertOk()
        ->assertJsonStructure(['token', 'user']);
});

it('returns 401 for invalid credentials', function () {
    User::factory()->create(['email' => 'user@example.com', 'password' => bcrypt('correct')]);

    $this->postJson('/api/auth/login', [
        'email' => 'user@example.com',
        'password' => 'wrong',
    ])->assertUnauthorized();
});

it('returns the authenticated user on /me', function () {
    $user = User::factory()->create();

    $this->actingAs($user, 'sanctum')
        ->getJson('/api/auth/me')
        ->assertOk()
        ->assertJsonPath('email', $user->email);
});

it('does not leak sensitive or internal user fields on /me', function () {
    $user = User::factory()->create([
        'is_admin' => true,
        'stripe_id' => 'cus_secret',
        'pm_type' => 'visa',
        'pm_last_four' => '4242',
        'postcode' => 'SW1A 1AA',
    ]);

    $user->subscriptions()->create([
        'type' => 'default',
        'stripe_id' => 'sub_secret',
        'stripe_status' => 'active',
        'stripe_price' => 'price_plus_monthly',
        'quantity' => 1,
    ]);

    $response = $this->actingAs($user, 'sanctum')
        ->getJson('/api/auth/me')
        ->assertOk();

    $payload = $response->json();

    expect(array_keys($payload))->toEqualCanonicalizing([
        'name',
        'email',
        'two_factor_confirmed_at',
        'tier',
        'subscription_cancelled',
        'subscription_cadence',
        'subscribed_at',
        'subscription_expires_at',
    ]);
});

it('reports subscription_cancelled=false for a user with no subscription', function () {
    $user = User::factory()->create();

    $this->actingAs($user, 'sanctum')
        ->getJson('/api/auth/me')
        ->assertOk()
        ->assertJsonPath('subscription_cancelled', false);
});

it('reports subscription_cancelled=false for an active paid subscription', function () {
    $user = User::factory()->create();

    $user->subscriptions()->create([
        'type' => 'default',
        'stripe_id' => 'sub_active',
        'stripe_status' => 'active',
        'stripe_price' => 'price_plus_monthly',
        'quantity' => 1,
    ]);

    $this->actingAs($user, 'sanctum')
        ->getJson('/api/auth/me')
        ->assertOk()
        ->assertJsonPath('subscription_cancelled', false);
});

it('reports subscription_cancelled=true once the subscription is set to end at period end', function () {
    $user = User::factory()->create();

    $user->subscriptions()->create([
        'type' => 'default',
        'stripe_id' => 'sub_cancelling',
        'stripe_status' => 'active',
        'stripe_price' => 'price_plus_monthly',
        'quantity' => 1,
        'ends_at' => now()->addDays(20),
    ]);

    $this->actingAs($user, 'sanctum')
        ->getJson('/api/auth/me')
        ->assertOk()
        ->assertJsonPath('subscription_cancelled', true);
});

it('exposes subscribed_at, cadence and renewal date for an active monthly subscription', function () {
    Plan::where('name', 'plus')->update([
        'stripe_price_id_monthly' => 'price_plus_monthly_test',
        'stripe_price_id_annual' => 'price_plus_annual_test',
    ]);

    $user = User::factory()->create();

    $subscribedAt = now()->subDays(10)->startOfSecond();
    $renewalAt = now()->addDays(20)->startOfSecond();

    $user->subscriptions()->create([
        'type' => 'default',
        'stripe_id' => 'sub_monthly_active',
        'stripe_status' => 'active',
        'stripe_price' => 'price_plus_monthly_test',
        'quantity' => 1,
        'current_period_end' => $renewalAt,
        'created_at' => $subscribedAt,
        'updated_at' => $subscribedAt,
    ]);

    $response = $this->actingAs($user, 'sanctum')
        ->getJson('/api/auth/me')
        ->assertOk()
        ->assertJsonPath('subscription_cancelled', false)
        ->assertJsonPath('subscription_cadence', 'monthly');

    expect($response->json('subscribed_at'))->toStartWith($subscribedAt->toDateString());
    expect($response->json('subscription_expires_at'))->toStartWith($renewalAt->toDateString());
});

it('reports cadence as annual when the active price is the annual one', function () {
    Plan::where('name', 'pro')->update([
        'stripe_price_id_monthly' => 'price_pro_monthly_test',
        'stripe_price_id_annual' => 'price_pro_annual_test',
    ]);

    $user = User::factory()->create();

    $user->subscriptions()->create([
        'type' => 'default',
        'stripe_id' => 'sub_annual_active',
        'stripe_status' => 'active',
        'stripe_price' => 'price_pro_annual_test',
        'quantity' => 1,
    ]);

    $this->actingAs($user, 'sanctum')
        ->getJson('/api/auth/me')
        ->assertOk()
        ->assertJsonPath('subscription_cadence', 'annual');
});

it('uses ends_at as the expiry date when subscription is cancelled', function () {
    $user = User::factory()->create();

    $endsAt = now()->addDays(15)->startOfSecond();
    $renewalAt = now()->addDays(30)->startOfSecond();

    $user->subscriptions()->create([
        'type' => 'default',
        'stripe_id' => 'sub_cancelling_with_period',
        'stripe_status' => 'active',
        'stripe_price' => 'price_plus_monthly',
        'quantity' => 1,
        'ends_at' => $endsAt,
        'current_period_end' => $renewalAt,
    ]);

    $response = $this->actingAs($user, 'sanctum')
        ->getJson('/api/auth/me')
        ->assertOk()
        ->assertJsonPath('subscription_cancelled', true);

    expect($response->json('subscription_expires_at'))->toStartWith($endsAt->toDateString());
});

it('returns null subscription metadata for users with no subscription', function () {
    $user = User::factory()->create();

    $this->actingAs($user, 'sanctum')
        ->getJson('/api/auth/me')
        ->assertOk()
        ->assertJsonPath('subscription_cancelled', false)
        ->assertJsonPath('subscription_cadence', null)
        ->assertJsonPath('subscribed_at', null)
        ->assertJsonPath('subscription_expires_at', null);
});

it('logs out and revokes the token', function () {
    $user = User::factory()->create();
    $token = $user->createToken('api')->plainTextToken;

    $this->withToken($token)
        ->postJson('/api/auth/logout')
        ->assertOk()
        ->assertJsonPath('message', 'Logged out.');

    expect($user->tokens()->count())->toBe(0);
});

it('returns null on /me when unauthenticated', function () {
    $response = $this->getJson('/api/auth/me')->assertOk();

    expect($response->getContent())->toBe('null');
});

it('returns 401 on protected routes without a token', function () {
    $this->postJson('/api/auth/logout')->assertUnauthorized();
});
