<?php

namespace App\Http\Controllers\SaasAdmin;

use App\Http\Controllers\Controller;
use App\Models\Article;
use App\Models\Blog;
use App\Models\Category;
use App\Models\Company;
use App\Models\CompanySubscription;
use App\Models\ContactMessage;
use App\Models\Coupon;
use App\Models\Course;
use App\Models\Domain;
use App\Models\Exam;
use App\Models\Frame;
use App\Models\Instructor;
use App\Models\Invoice;
use App\Models\ManageCertificate;
use App\Models\NewslatterSubscription;
use App\Models\Notification;
use App\Models\OfferBanner;
use App\Models\Page;
use App\Models\PaymentGateway;
use App\Models\Plan;
use App\Models\Quiz;
use App\Models\Setting;
use App\Models\Subscriber;
use App\Models\SubscriptionPlan;
use App\Models\Testimonial;
use App\Models\User;
use App\Repositories\UserRepository;
use App\Services\BackupService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Illuminate\View\View;

/**
 * Root-admin-only: creating a company here is the one and only way a
 * company gets onto the platform (Phase 2 — no self-serve signup). Every
 * company is created together with its first admin user and its
 * subdomain, live in the same request.
 */
class CompanyController extends Controller
{
    /**
     * Subdomain labels that would collide with the platform's own routes
     * or common infra hostnames if a company were allowed to claim them.
     */
    private const RESERVED_SLUGS = [
        'www', 'api', 'admin', 'saas-admin', 'internal', 'mail', 'ftp',
        'app', 'static', 'assets', 'cdn', 'ns1', 'ns2', 'root',
    ];

    public function index(): View
    {
        $companies = Company::with(['owner', 'domains'])->latest('id')->paginate(20);

        return view('saas-admin.companies.index', compact('companies'));
    }

    public function create(): View
    {
        $plans = SubscriptionPlan::where('status', 'active')->orderBy('price')->get();

        return view('saas-admin.companies.create', compact('plans'));
    }

    public function store(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'slug' => ['nullable', 'string', 'max:63', 'regex:/^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/'],
            'admin_name' => ['required', 'string', 'max:255'],
            'admin_email' => ['required', 'email', 'max:255', 'unique:users,email'],
            'admin_password' => ['required', 'string', 'min:8'],
            'subscription_plan_id' => ['nullable', 'exists:subscription_plans,id'],
            'started_at' => ['nullable', 'date'],
            'expires_at' => ['nullable', 'date', 'after:started_at'],
            'create_subdomain' => ['nullable', 'boolean'],
            'use_custom_domain' => ['nullable', 'boolean'],
            'custom_domain' => [
                'nullable', 'required_if:use_custom_domain,1', 'string', 'max:255',
                'regex:/^(?!-)[a-z0-9-]{1,63}(?<!-)(\.[a-z0-9-]{1,63})+$/i', 'unique:domains,hostname',
            ],
        ]);

        $wantsSubdomain = $request->boolean('create_subdomain');
        $wantsCustom = $request->boolean('use_custom_domain');

        if (!$wantsSubdomain && !$wantsCustom) {
            return back()
                ->withErrors(['create_subdomain' => __('Pick at least one: a subdomain, a custom domain, or both — a company needs somewhere to actually be reached.')])
                ->withInput();
        }

        $slug = $this->resolveUniqueSlug(($validated['slug'] ?? null) ?: $validated['name']);

        $company = DB::transaction(function () use ($request, $validated, $slug, $wantsSubdomain, $wantsCustom) {
            $company = Company::create([
                'name' => $validated['name'],
                'slug' => $slug,
                'status' => 'pending',
            ]);

            // If both are requested, the custom domain is the one shown as
            // primary — it's the actual brand, the subdomain is just an
            // always-working fallback underneath it.
            if ($wantsSubdomain) {
                Domain::create([
                    'company_id' => $company->id,
                    'hostname' => $slug . '.' . tenantRootDomain(),
                    'type' => 'subdomain',
                    'verified_at' => now(),
                    'ssl_status' => 'not_applicable', // covered by the wildcard cert, not issued per-domain
                    'is_primary' => !$wantsCustom,
                ]);
            }

            if ($wantsCustom) {
                Domain::create([
                    'company_id' => $company->id,
                    'hostname' => strtolower($validated['custom_domain']),
                    'type' => 'custom',
                    // Trusted here because a root admin is entering it directly,
                    // unlike the company's own self-service flow in Phase 3 which
                    // requires the TXT record first. Still won't resolve until
                    // its DNS is actually pointed at us.
                    'verified_at' => now(),
                    'ssl_status' => 'pending',
                    'is_primary' => true,
                ]);
            }

            $admin = UserRepository::create([
                'company_id' => $company->id,
                'name' => $validated['admin_name'],
                'email' => $validated['admin_email'],
                'phone' => '011' . random_int(100000000, 999999999),
                'password' => Hash::make($validated['admin_password']),
                'is_active' => true,
                'is_admin' => true,
                'email_verified_at' => now(),
            ]);
            $admin->assignRole('admin');

            $company->owner_user_id = $admin->id;
            $company->save();

            // footer_text and currency_position are NOT NULL with no
            // database-level default (see SettingSeeder — every other
            // Setting row in this app has always been created through it,
            // supplying these by hand). Every other field is genuinely
            // optional and left unset; the model's accessors already fall
            // back to a default asset for logo/favicon. The company fills
            // the rest in from Phase 4's branding screen.
            Setting::create([
                'company_id' => $company->id,
                'footer_text' => 'Powered by ' . config('app.name'),
                'currency_position' => 'Left',
            ]);

            if (!empty($validated['subscription_plan_id'])) {
                CompanySubscription::create([
                    'company_id' => $company->id,
                    'subscription_plan_id' => $validated['subscription_plan_id'],
                    'status' => 'active',
                    'started_at' => $validated['started_at'] ?? now(),
                    'expires_at' => $validated['expires_at'] ?? null,
                ]);
            }

            $company->recomputeStatus();

            $this->seedPaymentGateways($company);

            return $company;
        });

        $primaryDomain = $company->domains()->where('is_primary', true)->first();

        return to_route('saas-admin.companies.index')->with(
            'success',
            "{$company->name} is live at https://{$primaryDomain->hostname} — sign-in details were set exactly as entered."
        );
    }

    public function show(Company $company): View
    {
        $company->load(['owner', 'domains', 'subscriptions.subscriptionPlan']);
        $plans = SubscriptionPlan::where('status', 'active')->orderBy('price')->get();
        $adminUsers = $company->users()->where('is_admin', true)->withTrashed()->latest('id')->get();

        return view('saas-admin.companies.show', compact('company', 'plans', 'adminUsers'));
    }

    /**
     * Puts the company on a (possibly new) plan. The previous active
     * subscription, if any, is closed out as cancelled rather than
     * deleted — company_subscriptions is meant to stay a history, not
     * just today's snapshot.
     */
    public function assignSubscription(Request $request, Company $company): RedirectResponse
    {
        $validated = $request->validate([
            'subscription_plan_id' => ['required', 'exists:subscription_plans,id'],
            'started_at' => ['nullable', 'date'],
            'expires_at' => ['nullable', 'date', 'after:started_at'],
        ]);

        DB::transaction(function () use ($company, $validated) {
            $company->subscriptions()->where('status', 'active')->update(['status' => 'cancelled']);

            CompanySubscription::create([
                'company_id' => $company->id,
                'subscription_plan_id' => $validated['subscription_plan_id'],
                'status' => 'active',
                'started_at' => $validated['started_at'] ?? now(),
                'expires_at' => $validated['expires_at'] ?? null,
            ]);

            // Recompute rather than force-activate: a start date in the
            // future correctly lands the company on 'pending', not 'active'.
            $company->recomputeStatus();
        });

        return back()->with('success', __(':name is now :status.', ['name' => $company->name, 'status' => $company->fresh()->status]));
    }

    public function toggleStatus(Company $company): RedirectResponse
    {
        $company->isActive() ? $company->suspend() : $company->reactivate();

        return back()->with('success', __(':name is now :status.', [
            'name' => $company->name,
            'status' => $company->fresh()->status,
        ]));
    }

    /**
     * Every company-scoped table, purged directly, plus users. The
     * everything-underneath-a-course layer (chapters, contents, questions,
     * enrollments, transactions, and the rest) is deliberately NOT listed
     * here — the original schema already cascadeOnDelete()s all of that
     * from courses.id and users.id, so deleting Course and User rows
     * purges it automatically rather than needing ~20 more tables
     * enumerated by hand. Uploaded files (logos, course thumbnails) are
     * not removed, only the database rows that reference them.
     */
    private const TENANT_SCOPED_MODELS = [
        Course::class, Category::class, Instructor::class, Exam::class, Quiz::class,
        Coupon::class, Invoice::class, Blog::class, Article::class, Page::class,
        Testimonial::class, OfferBanner::class, Notification::class, Subscriber::class,
        ContactMessage::class, NewslatterSubscription::class, Plan::class, Frame::class,
        ManageCertificate::class, PaymentGateway::class, Setting::class,
        CompanySubscription::class, Domain::class,
    ];

    /**
     * Permanent, irreversible, and gated behind the company already being
     * expired — this is for actually cleaning up an account that's done,
     * not a shortcut around suspending one that's still current. A full
     * database backup is taken automatically first regardless: the one
     * button in this whole app that can't be undone by clicking another
     * button gets an undo path anyway.
     */
    public function destroy(Company $company, BackupService $backups): RedirectResponse
    {
        abort_if($company->slug === 'abic-e-learning', 403, __('The root company can\'t be deleted.'));
        abort_unless($company->isExpired(), 403, __('Only an expired company can be deleted — suspend or let its subscription lapse first.'));

        $backups->create();

        DB::transaction(function () use ($company) {
            foreach (self::TENANT_SCOPED_MODELS as $modelClass) {
                $usesSoftDeletes = in_array(\Illuminate\Database\Eloquent\SoftDeletes::class, class_uses_recursive($modelClass), true);

                $usesSoftDeletes
                    ? $modelClass::withTrashed()->where('company_id', $company->id)->forceDelete()
                    : $modelClass::where('company_id', $company->id)->delete();
            }

            User::withTrashed()->where('company_id', $company->id)->forceDelete();

            $company->delete();
        });

        return to_route('saas-admin.companies.index')->with(
            'success',
            __(':name and all its data have been permanently deleted. A full backup was taken first.', ['name' => $company->name])
        );
    }

    /**
     * Blank config for the same five gateways PaymentGatewaySeeder has
     * always seeded — but empty and inactive, never the seeder's demo
     * sandbox credentials. Each company connects its own account; money
     * from that company's students goes straight to that company, not to
     * ABIC_E-Learning. The icon image is the one thing worth sharing —
     * it's generic gateway branding, reused by reference from whichever
     * row already has it rather than duplicating the Media row per company.
     */
    private function seedPaymentGateways(Company $company): void
    {
        $blanks = [
            'paypal' => ['mode' => 'sandbox', 'app_id' => '', 'client_id' => '', 'client_secret' => ''],
            'stripe' => ['publishable_key' => '', 'secret_key' => ''],
            '2checkout' => ['merchant' => '', 'currency' => 'USD'],
            'aamarpay' => ['store_id' => '', 'signature_key' => '', 'currency' => 'BDT'],
            'razorpay' => ['key' => '', 'secret' => ''],
        ];

        foreach ($blanks as $name => $config) {
            $existingIcon = PaymentGateway::withoutGlobalScopes()->where('name', $name)->whereNotNull('media_id')->first();

            PaymentGateway::create([
                'company_id' => $company->id,
                'name' => $name,
                'config' => json_encode($config),
                'type' => 'sandbox',
                'is_active' => false,
                'media_id' => $existingIcon?->media_id,
            ]);
        }
    }

    private function resolveUniqueSlug(string $source): string
    {
        $base = Str::slug($source, '-');
        $base = $base !== '' ? $base : 'company';
        $slug = $base;
        $suffix = 2;

        while (in_array($slug, self::RESERVED_SLUGS, true) || Company::where('slug', $slug)->exists()) {
            $slug = $base . '-' . $suffix;
            $suffix++;
        }

        return $slug;
    }
}
