<?php

namespace App\Http\Controllers;

use App\Http\Requests\AccountUpdateRequest;
use App\Models\Location;
use App\Models\UserAddress;
use App\Services\Location\Geocoder;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;


class AccountController extends Controller
{
    /**
     *
     * landing page
     */
    public function index(): RedirectResponse
    {
        return Redirect::route('account.info');
    }

    /**
     *
     * info pane (name, email, timezone, etc.)
     */
    public function infoForm(Request $request)
    {
        $user = $request->user();

        return $this->frame('account.settings.info', [
            'title' => __('Account info'),
            'sub'   => __('Update your personal information and sign-in email.'),
            'user'  => $user,
        ]);
    }

    public function infoStore(AccountUpdateRequest $request): RedirectResponse
    {
        $user = $request->user();
        $user->fill($request->validated());

        if ($user->isDirty('email')) {
            $user->email_verified_at = null;
        }

        $user->save();

        return Redirect::route('account.info')
            ->with('toast', __('Account updated!'));
    }

    /**
     *
     * locale pane
     */
    public function localeForm(Request $request)
    {
        $user = $request->user();

        return $this->frame('account.settings.locale', [
            'title' => __('account.settings.locale.title'),
            'sub'   => __('account.settings.locale.subtitle'),
            'values' => [
                // language comes from column
                'language'    => $user->locale ?? config('app.locale'),
                // timezone comes from column (fallback to app timezone then UTC)
                'timezone'    => $user->timezone ?? config('app.timezone', 'UTC'),
                // the other three live in user.settings json
                'region'      => $user->getSetting('app.region', 'US'),
                'date_format' => $user->getSetting('app.date_format', 'mdy'),
                'time_format' => $user->getSetting('app.time_format', '12'),
            ],
            'options' => [
                'languages'    => config('kithkin.locales', []),       // optgroups
                'regions'      => config('kithkin.regions', []),       // optgroups
                'date_formats' => config('kithkin.date_formats', []),  // flat
                'time_formats' => config('kithkin.time_formats', []),  // flat
                'timezones'    => config('timezones', []),             // optgroups
            ],
        ]);
    }

    public function localeStore(Request $request): RedirectResponse
    {
        $allowedLocales = collect(config('kithkin.locales', []))
            ->flatMap(fn ($group) => is_array($group) ? array_keys($group) : [])
            ->unique()
            ->values()
            ->all();

        $allowedRegions = collect(config('kithkin.regions', []))
            ->flatMap(fn ($group) => is_array($group) ? array_keys($group) : [])
            ->unique()
            ->values()
            ->all();

        $allowedTimezones = collect(config('timezones', []))
            ->flatMap(fn ($group) => is_array($group) ? array_keys($group) : [])
            ->unique()
            ->values()
            ->all();

        $data = $request->validate([
            'language'    => ['required', Rule::in($allowedLocales)],
            'region'      => ['required', Rule::in($allowedRegions)],
            'date_format' => ['required', 'in:mdy,dmy,ymd'],
            'time_format' => ['required', 'in:12,24'],
            'timezone'    => ['required', 'string', Rule::in($allowedTimezones)],
        ]);

        $user = $request->user();

        // set language and timezone to their dedicated columns
        $user->locale   = $data['language'];
        $user->timezone = $data['timezone'];

        // everything else to json settings
        $user->setSettings([
            'app.region'      => $data['region'],
            'app.date_format' => $data['date_format'],
            'app.time_format' => $data['time_format'],
        ]);

        $user->save();

        // apply locale immediately
        app()->setLocale($user->locale);

        return Redirect::route('account.locale')
            ->with('toast', __('Settings saved!'));
    }

    /**
     *
     * addresses pane (home + work)
     */
    public function addressesForm(Request $request)
    {
        $user = $request->user();

        // try primary of each kind; fall back to the first of that kind
        $home = $user->addresses()->where('kind', 'home')->where('is_primary', 1)->first()
            ?? $user->addresses()->where('kind', 'home')->first();

        $work = $user->addresses()->where('kind', 'work')->where('is_primary', 1)->first()
            ?? $user->addresses()->where('kind', 'work')->first();

        // if neither is explicitly billing, fall back to "work if present, else home"
        $billing =
            ($home && $home->is_billing) ? 'home' :
            (($work && $work->is_billing) ? 'work' :
            ($work ? 'work' : 'home'));

        return $this->frame('account.settings.addresses', [
            'title'   => __('account.settings.addresses.title'),
            'sub'     => __('account.settings.addresses.subtitle'),
            'user'    => $user,
            'home'    => $home,
            'work'    => $work,
            'billing' => $billing,
        ]);
    }

    /**
     * save addresses pane
     */
    public function addressesStore(Request $request, Geocoder $geocoder): RedirectResponse
    {
        $user = $request->user();

        $data = $request->validate([
            'billing'            => ['required', 'in:home,work'],

            'home'               => ['sometimes', 'array'],
            'home.label'         => ['nullable', 'string', 'max:100'],
            'home.line1'         => ['nullable', 'string', 'max:255'],
            'home.line2'         => ['nullable', 'string', 'max:255'],
            'home.city'          => ['nullable', 'string', 'max:120'],
            'home.state'         => ['nullable', 'string', 'max:64'],
            'home.postal'        => ['nullable', 'string', 'max:32'],
            'home.country'       => ['nullable', 'string', 'max:64'],

            'work'               => ['sometimes', 'array'],
            'work.label'         => ['nullable', 'string', 'max:100'],
            'work.line1'         => ['nullable', 'string', 'max:255'],
            'work.line2'         => ['nullable', 'string', 'max:255'],
            'work.city'          => ['nullable', 'string', 'max:120'],
            'work.state'         => ['nullable', 'string', 'max:64'],
            'work.postal'        => ['nullable', 'string', 'max:32'],
            'work.country'       => ['nullable', 'string', 'max:64'],
        ]);

        DB::transaction(function () use ($user, $data, $geocoder) {

            $saved = []; // kind => UserAddress

            $save = function (string $kind, array $payload) use ($user, $geocoder, &$saved) {
                // short-circuit if nothing filled
                $filled = collect($payload)
                    ->only(['line1', 'line2', 'city', 'state', 'postal', 'country'])
                    ->filter(fn ($v) => filled($v));

                if ($filled->isEmpty()) {
                    return;
                }

                // upsert a single primary address of this kind
                $addr = UserAddress::updateOrCreate(
                    ['user_id' => $user->id, 'kind' => $kind, 'is_primary' => 1],
                    [
                        'label'   => $payload['label'] ?? ucfirst($kind),
                        'line1'   => $payload['line1'] ?? null,
                        'line2'   => $payload['line2'] ?? null,
                        'city'    => $payload['city'] ?? null,
                        'state'   => $payload['state'] ?? null,
                        'postal'  => $payload['postal'] ?? null,
                        'country' => $payload['country'] ?? null,
                    ]
                );

                $saved[$kind] = $addr;

                // geocode
                $singleLine = collect([
                    $addr->line1,
                    $addr->line2,
                    $addr->city,
                    $addr->state,
                    $addr->postal,
                    $addr->country,
                ])->filter()->implode(', ');

                if ($singleLine === '') {
                    return;
                }

                $norm = $geocoder->forward($singleLine);

                if (!$norm) {
                    return;
                }

                $loc = Location::findOrCreateNormalized($norm, $singleLine);
                $addr->location_id = $loc->id;
                $addr->save();
            };

            if (isset($data['home'])) {
                $save('home', $data['home']);
            }

            if (isset($data['work'])) {
                $save('work', $data['work']);
            }

            // billing flag: clear all billing flags for this user
            UserAddress::where('user_id', $user->id)->update(['is_billing' => 0]);

            // set billing on the chosen kind if we have that address
            $kind = $data['billing'];

            // if the chosen kind wasn't saved in this submission (e.g., user didn't fill it),
            // try to find an existing primary address of that kind.
            $billingAddr = $saved[$kind] ?? UserAddress::where('user_id', $user->id)
                ->where('kind', $kind)
                ->where('is_primary', 1)
                ->first();

            if ($billingAddr) {
                $billingAddr->is_billing = 1;
                $billingAddr->save();
            }
        });

        return Redirect::route('account.addresses')
            ->with('toast', __('Addresses updated!'));
    }

    /**
     * password pane
     */
    public function passwordForm(Request $request)
    {
        return $this->frame('account.settings.password', [
            'title' => __('Password'),
            'sub'   => __('Change your password.'),
            'user'  => $request->user(),
        ]);
    }

    /**
     * save password pane
     */
    public function passwordStore(Request $request): RedirectResponse
    {
        $data = $request->validate([
            'current_password' => ['required', 'current_password'],
            'password' => ['required', 'string', 'min:12', 'confirmed'],
        ]);

        $user = $request->user();

        $user->forceFill([
            'password' => Hash::make($data['password']),
        ])->save();

        return Redirect::route('account.password')
            ->with('toast', __('Password updated!'));
    }

    /**
     * delete pane
     */
    public function deleteForm(Request $request)
    {
        return $this->frame('account.settings.delete', [
            'title' => __('account.settings.delete.title'),
            'sub'   => __('account.settings.delete.subtitle'),
            'user'  => $request->user(),
        ]);
    }

    /**
     * delete confirmation modal/page
     */
    public function deleteConfirm(Request $request)
    {
        if ($request->header('HX-Request')) {
            return view('account.partials.delete-modal');
        }

        // no-js full page
        return $this->frame('account.settings.delete-confirm', [
            'title' => __('account.settings.delete-confirm.title'),
            'sub'   => __('account.settings.delete-confirm.subtitle'),
            'user'  => $request->user(),
        ]);
    }

    /**
     * delete the user's account
     */
    public function destroy(Request $request)
    {
        // validate: on failure
        // - full page: redirects to confirm page, flashes errors + input + toast
        // - htmx: returns 422 modal html + HX-Trigger toast
        $this->validateHtmx($request, [
            'password' => ['required', 'current_password'],
        ], [
            'bag'        => 'userDeletion',
            'redirect'   => 'account.delete.confirm', // no-js failure lands back on confirm
            'htmx_view'  => 'account.modals.delete',  // modal content to re-render on 422
            'toast_type' => 'error',                  // optional, default is already error
            // optional: override toast message (default is first validation message)
            // 'toast_message' => __('Please correct the password and try again.'),
            // optional: disable toast on validation failures:
            // 'toast' => false,
        ]);

        // passed validation, delete account
        $user = $request->user();

        Auth::logout();
        $user->delete();

        $request->session()->invalidate();
        $request->session()->regenerateToken();

        // success: for no-js, redirect home with toast
        // for htmx, send HX-Redirect + HX-Trigger (handled inside helper)
        return $this->redirectWithToast(
            $request,
            'dashboard', // or whatever route you want, could be a url('/') helper variant too
            __('Your account has been deleted.'),
            'success'
        );
    }

    /**
     * content frame handler
     */
    private function frame(?string $view = null, array $data = [])
    {
        return view('account.index', [
            'view'  => $view,
            'data'  => $data,
        ]);
    }
}
