<?php

declare(strict_types=1);

namespace App\Http\Controllers\Admin;

use App\Domain\Account\Enums\AccountStatus;
use App\Domain\Account\Models\Account;
use App\Domain\Account\Services\AccountProvisioner;
use App\Domain\Domain\Models\Domain;
use App\Domain\Package\Models\Package;
use App\Http\Controllers\Controller;
use App\Mail\BulkAdminMail;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Inertia\Inertia;
use Inertia\Response;

/**
 * Admin Bulk Account Modifier (WHM Modify Multiple Accounts muadili).
 * Filtreli toplu işlem: paket, quota, suspend, email gönderim, terminate.
 */
class BulkAccountController extends Controller
{
    private const ALLOWED_SORTS = ['created_at', 'disk_usage_bytes', 'username', 'status'];

    private const ACTIONS = [
        'change_package',
        'set_quota',
        'suspend',
        'unsuspend',
        'terminate',
        'send_email',
        'add_tag',
        'transfer_reseller',
    ];

    public function index(Request $request): Response
    {
        $filters = [
            'status' => $request->string('status')->trim()->value() ?: null,
            'package_id' => $request->integer('package_id') ?: null,
            'reseller_id' => $request->integer('reseller_id') ?: null,
            'search' => $request->string('search')->trim()->value() ?: null,
            'sort' => in_array($request->string('sort')->value(), self::ALLOWED_SORTS, true)
                ? $request->string('sort')->value()
                : 'created_at',
            'direction' => $request->string('direction')->value() === 'asc' ? 'asc' : 'desc',
        ];

        $query = Account::query()
            ->select('accounts.*')
            ->addSelect([
                'domains_count' => Domain::selectRaw('count(*)')
                    ->whereColumn('account_id', 'accounts.id'),
            ])
            ->with(['owner:id,name,email', 'package:id,name,limits'])
            ->when($filters['status'], fn ($q) => $q->where('status', $filters['status']))
            ->when($filters['package_id'], fn ($q) => $q->where('package_id', $filters['package_id']))
            ->when($filters['reseller_id'], fn ($q) => $q->where('owner_user_id', $filters['reseller_id']))
            ->when($filters['search'], function ($q) use ($filters) {
                $s = $filters['search'];
                $q->where(function ($inner) use ($s) {
                    $inner->where('username', 'like', "%{$s}%")
                        ->orWhere('email', 'like', "%{$s}%")
                        ->orWhere('primary_domain', 'like', "%{$s}%");
                });
            })
            ->orderBy($filters['sort'], $filters['direction']);

        $accounts = $query->paginate(50)->withQueryString()->through(fn ($a) => [
            'id' => $a->id,
            'username' => $a->username,
            'email' => $a->email,
            'primary_domain' => $a->primary_domain,
            'status' => $a->status->value,
            'status_label' => $a->status->label(),
            'status_color' => $a->status->color(),
            'package_id' => $a->package_id,
            'package_name' => $a->package?->name,
            'package_disk_mb' => $a->package?->diskMb(),
            'owner_user_id' => $a->owner_user_id,
            'owner_name' => $a->owner?->name ?? $a->owner?->email,
            'owner_email' => $a->owner?->email,
            'quota_bytes' => $a->quota_bytes,
            'disk_usage_bytes' => $a->disk_usage_bytes,
            'disk_usage_percent' => $a->diskUsagePercent(),
            'domains_count' => $a->domains_count ?? 0,
            'suspended_reason' => $a->suspended_reason,
            'created_at' => $a->created_at->format('d.m.Y H:i'),
        ]);

        $packages = Package::orderBy('name')->get(['id', 'name', 'limits']);

        $resellers = User::role('reseller')->get(['id', 'name', 'email'])->map(fn ($u) => [
            'id' => $u->id,
            'label' => ($u->name ?: $u->email)." ({$u->email})",
        ]);

        $stats = [
            'active' => Account::where('status', AccountStatus::Active)->count(),
            'suspended' => Account::where('status', AccountStatus::Suspended)->count(),
            'terminated' => Account::where('status', AccountStatus::Terminated)->count(),
            'pending' => Account::where('status', AccountStatus::Pending)->count(),
        ];

        return Inertia::render('Admin/Accounts/Bulk', compact(
            'accounts', 'packages', 'resellers', 'stats', 'filters'
        ));
    }

    public function apply(Request $request, AccountProvisioner $provisioner): RedirectResponse
    {
        $base = $request->validate([
            'account_ids' => ['required', 'array', 'min:1', 'max:500'],
            'account_ids.*' => ['integer', 'exists:accounts,id'],
            'action' => ['required', 'in:'.implode(',', self::ACTIONS)],
        ]);

        $action = $base['action'];

        // Per-action payload validation
        $payload = match ($action) {
            'change_package' => $request->validate(['package_id' => ['required', 'integer', 'exists:packages,id']]),
            'set_quota' => $request->validate(['quota_mb' => ['required', 'integer', 'min:0']]),
            'suspend' => $request->validate(['reason' => ['nullable', 'string', 'max:500']]),
            'terminate' => $request->validate(['confirm_string' => ['required', 'string', 'in:EVET KESIN SIL']]),
            'send_email' => $request->validate(['subject' => ['required', 'string', 'max:255'], 'body' => ['required', 'string']]),
            'add_tag' => $request->validate(['tag' => ['required', 'string', 'max:50']]),
            'transfer_reseller' => $request->validate(['new_reseller_id' => ['required', 'integer', 'exists:users,id']]),
            default => [],
        };

        $accounts = Account::whereIn('id', $base['account_ids'])->get();
        $count = 0;
        $failed = 0;
        $errors = [];

        $pkg = null;
        if ($action === 'change_package') {
            $pkg = Package::find($payload['package_id']);
        }

        foreach ($accounts as $account) {
            try {
                DB::transaction(function () use ($account, $action, $payload, $pkg, $request, &$count) {
                    $before = [
                        'status' => $account->status->value,
                        'package_id' => $account->package_id,
                        'quota_bytes' => $account->quota_bytes,
                        'suspended_reason' => $account->suspended_reason,
                        'owner_user_id' => $account->owner_user_id,
                    ];

                    switch ($action) {
                        case 'change_package':
                            $diskBytes = $pkg->diskMb() !== null ? $pkg->diskMb() * 1024 * 1024 : null;
                            $account->update([
                                'package_id' => $pkg->id,
                                'quota_bytes' => $diskBytes,
                            ]);
                            break;

                        case 'set_quota':
                            $account->update(['quota_bytes' => (int) $payload['quota_mb'] * 1024 * 1024]);
                            break;

                        case 'suspend':
                            if ($account->status === AccountStatus::Active || $account->status === AccountStatus::Pending) {
                                $account->update([
                                    'status' => AccountStatus::Suspended,
                                    'suspended_at' => now(),
                                    'suspended_reason' => $payload['reason'] ?? 'Toplu askıya alma',
                                ]);
                            }
                            break;

                        case 'unsuspend':
                            if ($account->status === AccountStatus::Suspended) {
                                $account->update([
                                    'status' => AccountStatus::Active,
                                    'suspended_at' => null,
                                    'suspended_reason' => null,
                                ]);
                            }
                            break;

                        case 'terminate':
                            // Gerçek terminate: AccountProvisioner ile sysapi user-terminate çağrılır,
                            // backup alınır, Linux user + vhost + DB + DNS + SSL teardown edilir.
                            // Failure'da status değişmez — caller catch eder.
                            $provisioner->terminate(
                                $account,
                                deleteData: true,
                                backupFirst: true,
                            );
                            break;

                        case 'send_email':
                            // Laravel Mail queue ile gerçek SMTP gönderim.
                            // SMTP config .env'de MAIL_* değişkenleriyle ayarlanır.
                            if (! empty($account->email)) {
                                try {
                                    Mail::to($account->email)->queue(
                                        new BulkAdminMail(
                                            account: $account,
                                            subject: (string) $payload['subject'],
                                            body: (string) $payload['body'],
                                            senderUserId: (int) $request->user()?->id,
                                        ),
                                    );
                                } catch (\Throwable $e) {
                                    Log::warning('bulk.email_queue_failed', [
                                        'account_id' => $account->id,
                                        'error' => $e->getMessage(),
                                    ]);
                                    throw $e;
                                }
                            }
                            break;

                        case 'add_tag':
                            // tags JSON kolonu — array append, deduplicate.
                            $tag = trim((string) $payload['tag']);
                            $existing = is_array($account->tags) ? $account->tags : [];
                            if (! in_array($tag, $existing, true)) {
                                $existing[] = $tag;
                                $account->update(['tags' => array_values(array_unique($existing))]);
                            }
                            break;

                        case 'transfer_reseller':
                            $account->update(['owner_user_id' => (int) $payload['new_reseller_id']]);
                            break;
                    }

                    $fresh = $account->fresh();
                    $after = $fresh ? [
                        'status' => $fresh->status->value,
                        'package_id' => $fresh->package_id,
                        'quota_bytes' => $fresh->quota_bytes,
                        'suspended_reason' => $fresh->suspended_reason,
                        'owner_user_id' => $fresh->owner_user_id,
                    ] : null;

                    DB::table('audit_log')->insert([
                        'user_id' => $request->user()?->id,
                        'ip_address' => $request->ip(),
                        'user_agent' => mb_substr($request->userAgent() ?? '', 0, 255),
                        'action' => 'account.bulk.'.$action,
                        'target_type' => 'Account',
                        'target_id' => $account->id,
                        'before' => json_encode($before),
                        'after' => json_encode($after),
                        'metadata' => json_encode([
                            'payload' => $action === 'terminate' ? ['confirmed' => true] : $payload,
                            'bulk_account_count' => null, // filled per-call
                        ]),
                        'outcome' => 'success',
                        'created_at' => now(),
                    ]);

                    $count++;
                });
            } catch (\Throwable $e) {
                $failed++;
                $errors[] = "{$account->username}: {$e->getMessage()}";
            }
        }

        $actionLabel = match ($action) {
            'change_package' => 'paket değiştirme',
            'set_quota' => 'kota ayarlama',
            'suspend' => 'askıya alma',
            'unsuspend' => 'askıdan çıkarma',
            'terminate' => 'sonlandırma',
            'send_email' => 'e-posta gönderimi (audit kaydı)',
            'add_tag' => 'etiket ekleme',
            'transfer_reseller' => 'bayi transferi',
            default => $action,
        };

        $message = "{$count} hesap üzerinde '{$actionLabel}' işlemi tamamlandı.";
        if ($failed > 0) {
            $message .= " {$failed} başarısız: ".implode('; ', array_slice($errors, 0, 3));
        }

        return back()->with($failed > 0 ? 'warning' : 'success', $message);
    }

    public function export(Request $request): \Illuminate\Http\Response
    {
        $filters = [
            'status' => $request->string('status')->trim()->value() ?: null,
            'package_id' => $request->integer('package_id') ?: null,
            'reseller_id' => $request->integer('reseller_id') ?: null,
            'search' => $request->string('search')->trim()->value() ?: null,
        ];

        $accounts = Account::query()
            ->with(['owner:id,name,email', 'package:id,name,limits'])
            ->when($filters['status'], fn ($q) => $q->where('status', $filters['status']))
            ->when($filters['package_id'], fn ($q) => $q->where('package_id', $filters['package_id']))
            ->when($filters['reseller_id'], fn ($q) => $q->where('owner_user_id', $filters['reseller_id']))
            ->when($filters['search'], function ($q) use ($filters) {
                $s = $filters['search'];
                $q->where(function ($inner) use ($s) {
                    $inner->where('username', 'like', "%{$s}%")
                        ->orWhere('email', 'like', "%{$s}%")
                        ->orWhere('primary_domain', 'like', "%{$s}%");
                });
            })
            ->orderBy('created_at', 'desc')
            ->get();

        $headers = ['id', 'username', 'owner_email', 'package', 'status', 'quota_mb', 'used_mb', 'created_at'];
        $rows = $accounts->map(fn ($a) => [
            $a->id,
            $a->username,
            $a->owner?->email ?? '',
            $a->package?->name ?? '',
            $a->status->value,
            $a->quota_bytes !== null ? round($a->quota_bytes / 1024 / 1024, 2) : '',
            round($a->disk_usage_bytes / 1024 / 1024, 2),
            $a->created_at->format('Y-m-d H:i:s'),
        ]);

        $csv = implode(',', $headers)."\n";
        foreach ($rows as $row) {
            $csv .= implode(',', array_map(fn ($v) => '"'.str_replace('"', '""', (string) $v).'"', $row))."\n";
        }

        $filename = 'onox-accounts-'.now()->format('Y-m-d').'.csv';

        return response($csv)
            ->header('Content-Type', 'text/csv; charset=UTF-8')
            ->header('Content-Disposition', "attachment; filename=\"{$filename}\"");
    }
}
