<?php

namespace MtcPharmacy\Subscriptions\Http\Controllers\Admin;

use AdminUser;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Mtc\Shop\Item as ShopItem;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Mtc\Modules\Members\Classes\Auth;
use Illuminate\Support\Facades\Session;
use Mtc\Modules\Members\Models\Member;
use Mtc\Shop\Item\Size as ShopItemSize;
use Illuminate\Database\Eloquent\Builder;
use MtcPharmacy\Subscriptions\Classes\Subscription;
use MtcPharmacy\Subscriptions\Classes\SubscriptionLog;
use MtcPharmacy\Subscriptions\Classes\SubscriptionItem;
use MtcPharmacy\Subscriptions\Classes\SubscriptionPreference;
use App\Support\PaginationPresenter;

class SubscriptionsController extends \App\Http\Controllers\Controller
{
    public function manage(Request $request)
    {
        // Validate filter inputs
        $request->validate([
            'id' => 'nullable|integer',
            'member_id' => 'nullable|integer',
            'status' => 'nullable|string|in:' . implode(',', Subscription::get_allowed_statuses()),
            'next_issue_date_start' => 'nullable|date',
            'next_issue_date_end' => 'nullable|date|after_or_equal:next_issue_date_start',
            'created_at_start' => 'nullable|date',
            'created_at_end' => 'nullable|date|after_or_equal:created_at_start',
            'per_page' => 'nullable|integer|min:1|max:250',
        ]);

        // Paging params
        $per_page = (int) $request->input('per_page', 20);
        if ($per_page < 1)   { $per_page = 20; }
        if ($per_page > 250) { $per_page = 250; }

        // Base query + filters
        $query = Subscription::query()
                             ->with([
                                 'member',
                                 'items.shop_item',
                                 'items.shop_item_size',
                                 'orders', // we sort/display these below; eager-load to avoid N+1
                             ])
                             ->withCount('orders'); // used for no_of_subscriptions efficiently

        $query = $this->filterSubscriptions($query, $request);

        // Paginate
        $subscriptions = $query->orderBy('created_at', 'desc')
                               ->paginate($per_page)                          // handles total, lastPage, etc
                               ->appends($request->except('page'));           // persist filters on links

        // Map to your display shape, but keep paginator wrapper
        $mapped = $subscriptions->getCollection()->map(function ($subscription) {
            // Return items as array for safe rendering in template
            $items = $subscription->items
                ->map(fn($item) => e($item->getDisplayName()))
                ->toArray();

            // Sort orders desc by id (already eager-loaded)
            $ordersSorted = $subscription->orders->sortByDesc('id');

            $orderCount = $subscription->orders_count ?? $subscription->orders()->count();
            $maxIssues = $subscription->max_number_of_issues;
            $issuesDisplay = $maxIssues ? "{$orderCount}/{$maxIssues}" : "{$orderCount}/∞";

            if ( isset($subscription->member) && !empty($subscription->member)) {
                $member = "{$subscription->member->full_name} ({$subscription->member_id})";
            } else {
                $member = "";
            }

            return [
                'id' => $subscription->id,
                'items' => $items,
                'orders' => $ordersSorted,
                'member' => $member,
                'no_of_subscriptions' => $issuesDisplay,
                'interval' => $subscription->interval_multiplier . ' ' . $subscription->interval,
                'status' => $subscription->status,
                'next_issue_date' => date_convert_db_to_dmy($subscription->next_issue_date),
                'created_date' => date_convert_db_to_dmy($subscription->created_at),
                'expiry_date' => date_convert_db_to_dmy($subscription->expiry_date),
                'actions' => [
                    'edit' => route('admin-subscriptions-edit', $subscription->id),
                ],
            ];
        });

        // Replace the paginator collection with the mapped version
        $subscriptions->setCollection($mapped);

        // Build a presenter that matches your Twig template API
        $pagination = PaginationPresenter::fromPaginator(
            $subscriptions,
            route('admin-subscriptions'),
            $request->except('page'),
            10,                 // pages to show window (like your $pagestoshow = 10)
            'page-link',        // link class (like your template expects)
            true,               // show page numbers
            true,                // show "Page X of Y"
            false
        );

        // Status counts
        $status_counts = [
            'all' => Subscription::count(),
        ];

        foreach (Subscription::get_allowed_statuses() as $status) {
            $status_counts[$status] = Subscription::where('status', $status)->count();
        }

        // Render the page
        return template('admin/subscriptions_manage.twig', [
            'selected_tab'   => 'manage',
            'title'          => 'Subscriptions',
            'subscriptions'  => $subscriptions,
            'statuses'       => Subscription::get_allowed_statuses(),
            'status_counts'  => $status_counts,
            'pagination'     => $pagination,
            'request'        => $request->all(),
            'total'          => $subscriptions->total(),
            'page_meta'      => ['title' => 'Subscriptions'],
        ]);
    }

    /**
     * Export subscriptions data to CSV
     *
     * @return \Symfony\Component\HttpFoundation\StreamedResponse
     */
    public function export(Request $request)
    {
        // Validate filter inputs (same as manage method)
        $request->validate([
            'id' => 'nullable|integer',
            'member_id' => 'nullable|integer',
            'status' => 'nullable|string|in:' . implode(',', Subscription::get_allowed_statuses()),
            'next_issue_date_start' => 'nullable|date',
            'next_issue_date_end' => 'nullable|date|after_or_equal:next_issue_date_start',
            'created_at_start' => 'nullable|date',
            'created_at_end' => 'nullable|date|after_or_equal:created_at_start',
        ]);

        $headers = [
            'Content-Type' => 'text/csv',
            'Content-Disposition' => 'attachment; filename="subscriptions-' . date('Y-m-d') . '-' . time() . '.csv"',
            'Pragma' => 'no-cache',
            'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
            'Expires' => '0'
        ];


        $subscriptions = Subscription::query()
            ->with(['member', 'items.shop_item', 'items.shop_item_size'])
            ->withCount('orders');

        $subscriptions = $this->filterSubscriptions($subscriptions, $request);

        $subscriptions = $subscriptions->orderBy('created_at', 'desc')->get();

        $callback = function () use ($subscriptions) {
            $file = fopen('php://output', 'w');

            // Add CSV headers
            fputcsv($file, [
                'Subscription ID',
                'Member ID',
                'Member Name',
                'Member Email',
                'Items',
                'Status',
                'Interval',
                'No. of Subscriptions',
                'Next Issue Date',
                'Expiry Date',
                'Created Date',
            ]);

            foreach ($subscriptions as $subscription) {
                // Use comma separator for CSV export (no HTML)
                $items = $subscription->items->map(function ($item) {
                    return $item->getDisplayName();
                })->implode(', ');

                $orderCount = $subscription->orders_count ?? $subscription->orders()->count();
                $maxIssues = $subscription->max_number_of_issues;
                $issuesDisplay = $maxIssues ? "{$orderCount}/{$maxIssues}" : "{$orderCount}/∞";

                fputcsv($file, [
                    'id' => $subscription->id,
                    'member_id' => $subscription->member->id,
                    'member' => $subscription->member->full_name,
                    'email' => $subscription->member->email,
                    'items' => $items,
                    'status' => ucfirst($subscription->status),
                    'interval' => $subscription->interval_multiplier . ' ' . $subscription->interval,
                    'no_of_subscriptions' => $issuesDisplay,
                    'next_issue_date' => date_convert_db_to_dmy($subscription->next_issue_date),
                    'expiry_date' => $subscription->expiry_date ? date_convert_db_to_dmy($subscription->expiry_date) : '',
                    'created_date' => date_convert_db_to_dmy($subscription->created_at),
                ]);
            }

            fclose($file);
        };

        return response()->stream($callback, 200, $headers);
    }

    protected function filterSubscriptions(Builder $query, Request $request)
    {
        if ($request->has('id') && !empty($request->input('id'))) {
            $query->where('id', $request->input('id'));
        }

        if ($request->has('member_id') && !empty($request->input('member_id'))) {
            $query->where('member_id', $request->input('member_id'));
        }

        if ($request->has('status') && !empty($request->input('status'))) {
            $query->where('status', $request->input('status'));
        }

        if ($request->has('next_issue_date_start') && !empty($request->input('next_issue_date_start'))) {
            $query->where('next_issue_date', '>=', $request->input('next_issue_date_start'));
        }

        if ($request->has('next_issue_date_end') && !empty($request->input('next_issue_date_end'))) {
            $query->where('next_issue_date', '<=', $request->input('next_issue_date_end'));
        }

        if ($request->has('created_at_start') && !empty($request->input('created_at_start'))) {
            $query->whereDate('created_at', '>=', $request->input('created_at_start'));
        }

        if ($request->has('created_at_end') && !empty($request->input('created_at_end'))) {
            $query->whereDate('created_at', '<=', $request->input('created_at_end'));
        }


        return $query;
    }

    public function create($member_id, Request $request)
    {
        $member = Member::findOrFail($member_id);

        $subscription = new Subscription();
        $subscription->member()->associate($member);
        $subscription->save();

        return redirect()->route('admin-subscriptions-edit', [$subscription->id]);
    }


    private function add_shop_item(Subscription $subscription, $item_to_add)
    {
        $parts = explode(':', $item_to_add, 2);
        $shop_item_id = $parts[0];
        $shop_item_size_id = $parts[1] ?? null;

        $shop_item = ShopItem::find($shop_item_id);
        $shop_item_size = $shop_item_size_id ? ShopItemSize::find($shop_item_size_id) : null;
        $si = SubscriptionItem::createFromShopItem($shop_item, $shop_item_size);

        $subscription->items()->save($si);
        Session::flash('success_message', ['type' => 'success', 'content' => 'Added.']);

        return redirect()->route('admin-subscriptions-edit', [$subscription->id]);
    }


    public function delete_item(int $subscription_item_id, Request $request)
    {
        $subscription_item = SubscriptionItem::findOrFail($subscription_item_id);

        $subscription_item->delete();
        Session::flash('success_message', ['type' => 'success', 'content' => 'Item deleted.']);

        return redirect()->route('admin-subscriptions-edit', [$subscription_item->subscription->id]);
    }


    public function update(int $subscription_id, Request $request)
    {
        $subscription = Subscription::findOrFail($subscription_id);
        $oldValues = $subscription->toArray();

        if ($request->input('button_pressed') == 'add_item') {

            return $this->add_shop_item($subscription, $request->input('item_to_add'));

        } else {

            $next_issue_date = $subscription->next_issue_date;

            $subscription->interval_multiplier = $request->input('interval_multiplier');
            $subscription->interval = $request->input('interval');
            $subscription->status = $request->input('status');
            $subscription->max_number_of_issues = $request->input('max_number_of_issues');
            $subscription->expiry_date = $request->input('expiry_date');
            $subscription->next_issue_date = $request->input('next_issue_date');
            $subscription->can_be_paused = $request->input('can_be_paused');

            if (!$request->input('can_be_paused')) {
                $subscription->can_be_paused = false;
            }

            $errors_bag = $subscription->validate()->errors();

            // check if next issue date is a valid date
            if (!Carbon::canBeCreatedFromFormat($next_issue_date, 'Y-m-d')) {
                $errors_bag->add('next_issue_date', 'Next issue date must be a valid date in Y-m-d format');
            } else {
                $next_issue_date_set = Carbon::parse($request->input('next_issue_date'));
                // Get current date plus buffer days
                $currentDatePlusBuffer = now()->addDays(Subscription::ORDER_CREATE_DAYS_BEFORE_DISPATCH + 1);

                // Convert the next_issue_date to a Carbon instance if it's not already
                // and ensure we're comparing just the dates without time parts
                $nextIssueDate = $next_issue_date_set->startOfDay();
                $currentDatePlusBufferDay = $currentDatePlusBuffer->copy()->startOfDay();
                // 45 days from now`

                $maxRescheduleDays = config('subscription.max_reschedule_days', 45);

                // If subscription issue date is already in the past, then add max reschedule days to the current date
                if (Carbon::parse($next_issue_date) < now()) {
                    $maxIssueDate = now()->addDays($maxRescheduleDays)->startOfDay();
                } else {
                    $maxIssueDate = Carbon::parse($next_issue_date)->addDays($maxRescheduleDays)->startOfDay();
                }

                // Now compare just the dates
                if ($nextIssueDate->lt($currentDatePlusBufferDay) || $nextIssueDate->gt($maxIssueDate)) {
                    // The date is too far in the future
                    $errors_bag->add('next_issue_date', 'Next dispatch date must be greater than ' . $currentDatePlusBufferDay->format('d/m/Y') . ' and less than ' . $maxIssueDate->format('d/m/Y'));
                }
            }

            if ($errors_bag->isEmpty()) {
                if ($subscription->status != $request->get('status')) {
                    $subscription->emailSubscriptionUpdates(
                        'Your subscription has been ' . $request->get('status')
                    );
                }

                $subscription->save();

                Session::flash('success_message', ['type' => 'success', 'content' => 'Saved.']);
            } else {
                $form_errors = \App\FlashMessage::getImplodedMessages($errors_bag);
                Session::flash('form_errors', $form_errors);
            }
        }

        return redirect()->route('admin-subscriptions-edit', [$subscription->id]);
    }

    public function edit(int $subscription_id, Request $request)
    {
        $subscription = Subscription::findOrFail($subscription_id);
        $subscription->load(['items', 'logs']);

        $messages = $this->getFormMessages();

        //Attach preferences to sizes
        $subscription->items = $subscription->items->map(function ($sub) {

            $sub->shop_item = false;
            $item = \Mtc\Shop\Item::find((int) $sub->shop_item_id);

            if (!empty($item)) {
                // Add subscription prices to sizes
                $item->mapSizePricesToSubscriptions();
                $sub->shop_item = $item;
            }

            return $sub;
        });

        $adminuser = new AdminUser();

        return template('admin/subscription_edit.twig', [
            'subscription' => $subscription,
            'is_admin' => $adminuser->isMtcAdmin(),
            'selected_tab' => 'edit',
            'title' => 'Edit Subscription #' . $subscription->id,
            'page_meta' => [
                'title' => 'Edit Subscription #' . $subscription->id,
            ],
            'allowed_statuses' => array_combine(
                Subscription::get_allowed_statuses(),
                Subscription::get_allowed_statuses()
            ),
            'allowed_intervals' => array_combine(
                Subscription::get_allowed_intervals(),
                Subscription::get_allowed_intervals()
            ),
            'messages' => $messages,
            'allowed_items' => SubscriptionItem::getAllowedItems(),
            'cancellation_reasons' => Subscription::CANCELLATION_REASONS,
        ]);
    }

    private function getFormMessages()
    {
        $messages = [];

        if ($success_message = Session::pull('success_message')) {
            $messages[] = $success_message;
        }

        if ($form_errors = Session::pull('form_errors')) {
            $messages[] = ['type' => 'error', 'content' => 'Something went wrong!'];
            $messages['errors'] = $form_errors;
        }

        return $messages;
    }


    public function data()
    {
        $offset = request()->input('start', 0);
        $limit = request()->input('length', 10);
        $draw = request()->input('draw', 1);

        $subscriptions = Subscription::query()
            ->with(['member', 'items.shop_item', 'items.shop_item_size'])
            ->withCount('orders')
            ->offset($offset)
            ->limit($limit)
            ->get()
        ;

        $data = [];
        foreach ($subscriptions as $subscription) {

            $subscription_items_array = [];
            foreach ($subscription->items as $subscription_item) {
                $subscription_items_array[] = $subscription_item->getDisplayName();
            }

            $subscription_items_string = implode('<br />', array_map('htmlspecialchars', $subscription_items_array));

            $sim_subs = [];
            foreach ($subscription->getSimilarSubscriptions() as $sim_sub) {
                $sim_subs[$sim_sub->id] = "#{$sim_sub->id}";
            }

            $orderCount = $subscription->orders_count;
            $maxIssues = $subscription->max_number_of_issues;
            $issuesDisplay = $maxIssues ? "{$orderCount}/{$maxIssues}" : "{$orderCount}/∞";

            $data[] = [
                'id' => $subscription->id,
                'items' => $subscription_items_string,
                'member' => "{$subscription->member->full_name} ({$subscription->member->email})",
                'interval_multiplier' => $subscription->interval_multiplier,
                'no_of_subscriptions' => $issuesDisplay,
                'interval' => $subscription->interval,
                'status' => $subscription->status,
                'can_be_paused' => $subscription->getCanBePausedString(),
                'similar_subscriptions' => implode(', ', $sim_subs),
                'next_issue_date' => date_convert_db_to_dmy($subscription->next_issue_date),
                'created_date' => date_convert_db_to_dmy($subscription->created_at),
                'expiry_date' => date_convert_db_to_dmy($subscription->expiry_date),
                'actions' => [
                    'edit' => route('admin-subscriptions-edit', $subscription->id),
                ],
            ];

        }

        $records_total = Subscription::count();

        return [
            "draw" => $draw,
            "recordsTotal" => $records_total,
            "recordsFiltered" => $records_total,
            'data' => $data,
        ];
    }

    public function set_paused($subscription_id, Request $request)
    {
        $subscription = Subscription::findOrFail($subscription_id);

        if ($subscription->isActive()) {
            $subscription->setPaused();

            $subscription->emailSubscriptionUpdates(
                'Your subscription has been paused',
                'pause_subscription'
            );

            return response()->json(['success' => true, 'message' => 'Subscription paused successfully']);
        }

        return response()->json(['success' => false, 'message' => 'Subscription is not active'], 422);
    }

    public function set_active($subscription_id, Request $request)
    {
        $subscription = Subscription::findOrFail($subscription_id);

        if ($subscription->isPaused()) {
            $subscription->setActive();

            $subscription->emailSubscriptionUpdates(
                'Your subscription has been activated'
            );

            return response()->json(['success' => true, 'message' => 'Subscription activated successfully']);
        }

        return response()->json(['success' => false, 'message' => 'Subscription is not paused'], 422);
    }

    public function set_cancelled($subscription_id, Request $request)
    {
        $subscription = Subscription::findOrFail($subscription_id);

        if ($request->has('cancel_reason') && $request->get('cancel_reason') != '') {
            $cancelReason = strip_tags($request->input('cancel_reason'));
            $cancelReason = clean_page($cancelReason);

            $subscription->setCancelled($cancelReason);

            $subscription->emailSubscriptionUpdates(
                'Your subscription has been cancelled',
                'cancel_subscription'
            );

            return response()->json(['success' => true, 'message' => 'Subscription cancelled successfully']);
        }

        return response()->json(['success' => false, 'message' => 'A cancellation reason is required'], 422);
    }

    public function generate_order($subscription_id, Request $request)
    {
        $subscription = Subscription::findOrFail($subscription_id);

        $ignore_checks = (bool) $request->input('ignore_checks', false);
        $attempt_token_payment = (bool) $request->input('attempt_token_payment', false);

        Log::info('Admin manual order generation', [
            'subscription_id' => $subscription_id,
            'ignore_checks' => $ignore_checks,
            'attempt_token_payment' => $attempt_token_payment,
            'admin_id' => $_SESSION['adminId'] ?? null,
        ]);

        $new_order = $subscription->generateNewOrder($ignore_checks, $attempt_token_payment);

        return [
            'generated_order_id' => $new_order->id ?? null,
            'payment_captured' => $new_order->paid ?? null,
        ];
    }
}
