<?php

namespace App\Http\Controllers;

use App\Lib\Backend;
use App\Models\Appointment;
use App\Models\AppSession;
use App\Models\ClientSMS;
use App\Models\Facility;
use App\Models\ProProAccess;
use App\Models\Ticket;
use DateTime;

use App\Models\Client;
use App\Models\Bill;
use App\Models\Measurement;
use App\Models\Note;
use App\Models\Pro;
use App\Models\ProTransaction;
use GuzzleHttp\Cookie\CookieJar;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;

class HomeController extends Controller
{

    public function confirmSmsAuthToken(Request $request)
    {
        return view('app/confirm_sms_auth_token');
    }

    public function setPassword(Request $request)
    {
        return view('app/set_password');
    }

    public function setSecurityQuestions(Request $request)
    {
        return view('app/set_security_questions');
    }

    public function postConfirmSmsAuthToken(Request $request)
    {

        try {

            $url = config('stag.backendUrl') . '/session/confirmSmsAuthToken';

            $data = [
                'cellNumber' => $request->input('cellNumber'),
                'token' => $request->input('token'),
            ];

            $response = Http::asForm()
                ->withHeaders(['sessionKey' => $request->cookie('sessionKey')])
                ->post($url, $data)
                ->json();

            if (!isset($response['success']) || !$response['success']) {
                $message = 'API error';
                if (isset($response['error'])) {
                    $message = $response['error'];
                    if (isset($response['path'])) $message .= ': ' . $response['path'];
                } else if (isset($response['message'])) $message = $response['message'];
                return redirect('/confirm_sms_auth_token')
                    ->withInput()
                    ->with('message', $message);
            }

            return redirect('/');
        } catch (\Exception $e) {
            return redirect()->back()
                ->with('message', 'Unable to process your request at the moment. Please try again later.')
                ->withInput($request->input());
        }
    }

    public function resendSmsAuthToken(Request $request)
    {

        try {

            $url =  config('stag.backendUrl') . '/session/resendSmsAuthToken';

            $data = [];

            $response = Http::asForm()
                ->withHeaders(['sessionKey' => $request->cookie('sessionKey')])
                ->post($url, $data)
                ->json();

            if (!isset($response['success']) || !$response['success']) {
                $message = 'API error';
                if (isset($response['error'])) {
                    $message = $response['error'];
                    if (isset($response['path'])) $message .= ': ' . $response['path'];
                } else if (isset($response['message'])) $message = $response['message'];
                return redirect('/confirm_sms_auth_token')
                    ->withInput()
                    ->with('message', $message);
            }

            return redirect()->back()->withInput()->with('message', "SMS Auth Token sent.");
        } catch (\Exception $e) {
            return redirect()->back()
                ->with('message', 'Unable to process your request at the moment. Please try again later.')
                ->withInput($request->input());
        }
    }

    public function postSetPassword(Request $request)
    {
        try {

            $url =  config('stag.backendUrl') . '/pro/selfPutPassword';

            $data = [
                'newPassword' => $request->input('newPassword'),
                'newPasswordConfirmation' => $request->input('newPasswordConfirmation'),
            ];

            $response = Http::asForm()
                ->withHeaders(['sessionKey' => $request->cookie('sessionKey')])
                ->post($url, $data)
                ->json();

            if (!isset($response['success']) || !$response['success']) {
                $message = 'API error';
                if (isset($response['error'])) {
                    $message = $response['error'];
                    if (isset($response['path'])) $message .= ': ' . $response['path'];
                } else if (isset($response['message'])) $message = $response['message'];
                return redirect('/set_password')
                    ->withInput()
                    ->with('message', $message);
            }

            return redirect('/');
        } catch (\Exception $e) {
            return redirect()->back()
                ->with('message', 'Unable to process your request at the moment. Please try again later.')
                ->withInput($request->input());
        }
    }
    public function postSetSecurityQuestions(Request $request)
    {

        try {

            $url = env('BACKEND_URL', 'http://localhost:8080/api') . '/pro/selfPutSecurityQuestions';

            $data = [
                'securityQuestion1' => $request->input('securityQuestion1'),
                'securityAnswer1' => $request->input('securityAnswer1'),
                'securityQuestion2' => $request->input('securityQuestion2'),
                'securityAnswer2' => $request->input('securityAnswer2'),
            ];

            $response = Http::asForm()
                ->withHeaders(['sessionKey' => $request->cookie('sessionKey')])
                ->post($url, $data)
                ->json();

            if (!isset($response['success']) || !$response['success']) {
                $message = 'API error';
                if (isset($response['error'])) {
                    $message = $response['error'];
                    if (isset($response['path'])) $message .= ': ' . $response['path'];
                } else if (isset($response['message'])) $message = $response['message'];
                return redirect('/set_password')
                    ->withInput()
                    ->with('message', $message);
            }

            return redirect('/');
        } catch (\Exception $e) {
            return redirect()->back()
                ->with('message', 'Unable to process your request at the moment. Please try again later.')
                ->withInput($request->input());
        }
    }

    public function dashboard(Request $request)
    {

        //patients where performer is the mcp
        $performer = $this->performer();
        $performerProID = $performer->pro->id;
        $isAdmin = ($performer->pro->pro_type === 'ADMIN');

        $keyNumbers  = [];

        $queryClients = $this->performer()->pro->getAccessibleClientsQuery();

        $keyNumbers['totalPatients'] = $queryClients->count();

        // patientNotSeenYet
        $patientNotSeenYet = $queryClients
            ->where(function ($query) use ($performer) {     // own patient and primary OB visit pending
                $query->where('mcp_pro_id', $performer->pro->id)
                    ->where('has_mcp_done_onboarding_visit', '!=', 'YES');
            })
            ->orWhere(function ($query) {   // mcp of any client program and program OB pending
                $query->where(function ($_query) {
                    $_query->select(DB::raw('COUNT(id)'))
                        ->from('client_program')
                        ->whereColumn('client_id', 'client.id')
                        ->where('has_mcp_done_onboarding_visit', '!=', 'YES');
                }, '>=', 1);
            })->count();
        $keyNumbers['patientsNotSeenYet'] = $patientNotSeenYet;

        $pendingBillsToSign = Bill::where(function ($query) use ($performerProID) {
            $query->where('hcp_pro_id', $performerProID)->where('is_signed_by_hcp', false)->where('is_cancelled', false);
        })
            ->orWhere(function ($query) use ($performerProID) {
                $query->where('cm_pro_id', $performerProID)->where('is_signed_by_cm', false)->where('is_cancelled', false);;
            })->orWhere(function ($query) use ($performerProID) {
                $query->where('rme_pro_id', $performerProID)->where('is_signed_by_rme', false)->where('is_cancelled', false);;
            })->orWhere(function ($query) use ($performerProID) {
                $query->where('rmm_pro_id', $performerProID)->where('is_signed_by_rmm', false)->where('is_cancelled', false);;
            })->count();

        $keyNumbers['pendingBillsToSign'] = $pendingBillsToSign;

        $pendingNotesToSign = Note::where(function ($query) use ($performerProID) {
            $query->where('hcp_pro_id', $performerProID)->where('is_signed_by_hcp', false)->where('is_cancelled', false);;
        })
            ->orWhere(function ($query) use ($performerProID) {
                $query->where('ally_pro_id', $performerProID)->where('is_signed_by_ally', false)->where('is_cancelled', false);;
            })->count();

        $keyNumbers['pendingNotesToSign'] = $pendingNotesToSign;

        // open tickets
        $keyNumbers['numOpenTickets'] = Ticket::where('is_open', true)
            ->where(function ($q) use ($performerProID) {
                $q->where('assigned_pro_id', $performerProID)
                    ->orWhere('manager_pro_id', $performerProID)
                    ->orWhere('ordering_pro_id', $performerProID)
                    ->orWhere('initiating_pro_id', $performerProID);
            })
            ->count();

        // num measurements that need stamping
        $keyNumbers['measurementsToBeStamped'] = ($this->performer()->pro->pro_type === 'ADMIN' ? '-' : count($this->performer()->pro->getMeasurements(true)));

        $reimbursement = [];
        $reimbursement["currentBalance"] =  $performer->pro->balance;
        $reimbursement["nextPaymentDate"] = '--';
        $lastPayment = ProTransaction::where('pro_id', $performerProID)->where('plus_or_minus', 'PLUS')->orderBy('created_at', 'DESC')->first();
        if ($lastPayment) {
            $reimbursement["lastPayment"] =  $lastPayment->amount;
            $reimbursement["lastPaymentDate"] = $lastPayment->created_at;
        } else {
            $reimbursement["lastPayment"] = '--';
            $reimbursement["lastPaymentDate"] = '--';
        }

        //if today is < 15th, next payment is 15th, else nextPayment is
        $today = strtotime(date('Y-m-d'));
        $todayDate = date('j', $today);

        $todayMonth =  date('m', $today);
        $todayYear = date('Y', $today);
        if ($todayDate < 15) {
            $nextPaymentDate = new DateTime();
            $nextPaymentDate->setDate($todayYear, $todayMonth, 15);
            $reimbursement['nextPaymentDate'] = $nextPaymentDate->format('m/d/Y');
        } else {
            $nextPaymentDate = new \DateTime();
            $lastDayOfMonth = date('t', $today);
            $nextPaymentDate->setDate($todayYear, $todayMonth, $lastDayOfMonth);
            $reimbursement['nextPaymentDate'] = $nextPaymentDate->format('m/d/Y');
        }

        //expectedPay
        $expectedForHcp = DB::select(DB::raw("SELECT coalesce(SUM(hcp_expected_payment_amount),0) as expected_pay FROM bill WHERE hcp_pro_id = :performerProID  AND has_hcp_been_paid = false AND is_signed_by_hcp IS TRUE AND is_cancelled = false"), ['performerProID' => $performerProID])[0]->expected_pay;
        $expectedForCm = DB::select(DB::raw("SELECT coalesce(SUM(cm_expected_payment_amount),0) as expected_pay  FROM bill WHERE cm_pro_id = :performerProID  AND has_cm_been_paid = false AND is_signed_by_cm IS TRUE AND is_cancelled = false"), ['performerProID' => $performerProID])[0]->expected_pay;
        $expectedForRme = DB::select(DB::raw("SELECT coalesce(SUM(rme_expected_payment_amount),0) as expected_pay  FROM bill WHERE rme_pro_id = :performerProID  AND has_rme_been_paid = false AND is_signed_by_rme IS TRUE AND is_cancelled = false"), ['performerProID' => $performerProID])[0]->expected_pay;
        $expectedForRmm = DB::select(DB::raw("SELECT coalesce(SUM(rmm_expected_payment_amount),0) as expected_pay  FROM bill WHERE rmm_pro_id = :performerProID  AND has_rmm_been_paid = false AND is_signed_by_rmm IS TRUE AND is_cancelled = false"), ['performerProID' => $performerProID])[0]->expected_pay;
        $expectedForNa = DB::select(DB::raw("SELECT coalesce(SUM(na_expected_payment_amount),0) as expected_pay  FROM bill WHERE na_pro_id = :performerProID  AND has_na_been_paid = false AND is_signed_by_hcp IS TRUE AND is_cancelled = false"), ['performerProID' => $performerProID])[0]->expected_pay;

        $totalExpectedAmount =  $expectedForHcp + $expectedForCm + $expectedForRme + $expectedForRmm + $expectedForNa;
        $reimbursement['nextPaymentAmount'] =  $totalExpectedAmount;

        $milliseconds = strtotime(date('Y-m-d')) . '000';

        $measurements = $performer->pro->getMeasurements();

        return view('app/dashboard', compact('keyNumbers', 'reimbursement', 'milliseconds', 'measurements'));
    }

    public function dashboardAppointments(Request $request, $from, $to) {

        $performer = $this->performer();
        $performerProID = $performer->pro->id;
        $isAdmin = ($performer->pro->pro_type === 'ADMIN');

        $appointments = Appointment::where("start_time", '>=', $from)->where("start_time", '<=', $to.' 23:59:00+00');

        if(!$isAdmin) {
            $appointments = $appointments->where("pro_id", $performerProID);
        }

        $appointments = $appointments
            ->orderBy('start_time', 'asc')
            ->get();

        foreach ($appointments as $appointment) {
            $date = explode(" ", $appointment->start_time)[0];
            $appointment->milliseconds = strtotime($date) . '000';
            $appointment->newStatus = $appointment->status;

            $appointment->dateYMD = date('Y-m-d', strtotime($appointment->raw_date));
            $appointment->clientName = $appointment->client->displayName();
            $appointment->clientInitials = substr($appointment->client->name_first, 0, 1) . substr($appointment->client->name_last, 0, 1);
            $appointment->proInitials = substr($appointment->pro->name_first, 0, 1) . substr($appointment->pro->name_last, 0, 1);
            $appointment->friendlyStartTime = friendly_time($appointment->raw_start_time);
            $appointment->friendlyEndTime = friendly_time($appointment->raw_end_time);
            $appointment->clientSummary = friendly_date_time($appointment->client->dob, false) . ' (' .
                $appointment->client->age_in_years . ' y.o' .
                ($appointment->client->sex ? ' ' . $appointment->client->sex : '') .
                ')';

            $appointment->started = false;
            $appointment->inHowManyHours = date_diff(date_create('now'), date_create($appointment->start_time), false)
                ->format('%R%h h, %i m');
            if ($appointment->inHowManyHours[0] === '-') {
                $appointment->inHowManyHours = substr($appointment->inHowManyHours, 1) . ' ago';
                $appointment->started = true;
            } else {
                $appointment->inHowManyHours = 'Appt. in ' . substr($appointment->inHowManyHours, 1);
            }
            $appointment->clientUid = $appointment->client->uid;
            $appointment->proUid = $appointment->pro->uid;
            $appointment->proName = $appointment->pro->displayName();
        }

        return json_encode($appointments);
    }

    public function dashboardMeasurements(Request $request, $filter) {
        $measurements = $this->performer()->pro->getMeasurements($filter === 'NEED_ACK');
        return json_encode($measurements);
    }

    public function patients(Request $request, $filter = '')
    {
        $performer = $this->performer();
        $query = $performer->pro->getAccessibleClientsQuery();

        switch ($filter) {
            case 'not-yet-seen':
                $query = $query
                    ->where(function ($query) use ($performer) {
                        $query
                            ->where(function ($query) use ($performer) {     // own patient and primary OB visit pending
                                $query->where('mcp_pro_id', $performer->pro->id)
                                    ->where('has_mcp_done_onboarding_visit', '<>', 'YES');
                            })
                            ->orWhere(function ($query) use ($performer) {   // mcp of any client program and program OB pending
                                $query->select(DB::raw('COUNT(id)'))
                                    ->from('client_program')
                                    ->whereColumn('client_id', 'client.id')
                                    ->where('mcp_pro_id', $performer->pro->id)
                                    ->where('has_mcp_done_onboarding_visit', '<>', 'YES');
                            }, '>=', 1);
                    });
                break;

                // more cases can be added as needed
            default:
                break;
        }
        $patients = $query->orderBy('id', 'desc')->paginate(500);
        return view('app/patients', compact('patients', 'filter'));
    }

    public function patientsSuggest(Request $request)
    {

        $pro = $this->pro;
        $term = $request->input('term') ? trim($request->input('term')) : '';
        if (empty($term)) return '';
        $clientQuery= Client::where(function ($q) use ($term) {
            $q->where('name_first', 'ILIKE', '%' . $term . '%')
                ->orWhere('name_last', 'ILIKE', '%' . $term . '%')
                ->orWhere('cell_number', 'ILIKE', '%' . $term . '%');
        });

        if($pro->pro_type != 'ADMIN'){
            $clientQuery->whereIn('id', $pro->getMyClientIds());
        }

        $clients = $clientQuery->get();
        return view('app/patient-suggest', compact('clients'));
    }

    public function pharmacySuggest(Request $request)
    {
        $term = $request->input('term') ? trim($request->input('term')) : '';
        if (empty($term)) return '';
        $term = strtolower($term);
        $pharmacies = Facility::where('facility_type', 'Pharmacy')
            ->where(function ($q) use ($term) {
                $q->orWhereRaw('LOWER(name::text) LIKE ?', ['%' . $term . '%'])
                    ->orWhereRaw('LOWER(address_line1::text) LIKE ?', ['%' . $term . '%'])
                    ->orWhereRaw('LOWER(address_line2::text) LIKE ?', ['%' . $term . '%'])
                    ->orWhereRaw('LOWER(address_city::text) LIKE ?', ['%' . $term . '%'])
                    ->orWhereRaw('LOWER(address_state::text) LIKE ?', ['%' . $term . '%'])
                    ->orWhereRaw('LOWER(phone::text) LIKE ?', ['%' . $term . '%'])
                    ->orWhereRaw('LOWER(address_zip::text) LIKE ?', ['%' . $term . '%']);
            })
            ->orderBy('name', 'asc')
            ->orderBy('address_line1', 'asc')
            ->orderBy('address_city', 'asc')
            ->orderBy('address_state', 'asc')
            ->get();
        return view('app/pharmacy-suggest', compact('pharmacies'));
    }

    public function proSuggest(Request $request) {
        $term = $request->input('term') ? trim($request->input('term')) : '';
        if (empty($term)) return '';
        $term = strtolower($term);
        $pros = Pro::where(function ($q) use ($term) {
            $q->orWhereRaw('LOWER(name_first::text) LIKE ?', ['%' . $term . '%'])
                ->orWhereRaw('LOWER(name_last::text) LIKE ?', ['%' . $term . '%'])
                ->orWhereRaw('cell_number LIKE ?', ['%' . $term . '%']);
        });
        if($this->performer->pro && $this->performer->pro->pro_type != 'ADMIN'){
            $accessiblePros = ProProAccess::where('owner_pro_id', $this->performer->pro->id);
            $accessibleProIds = [];
            foreach($accessiblePros as $accessiblePro){
                $accessibleProIds[] = $accessiblePro->id;
            }
            $accessibleProIds[] = $this->performer->pro->id;
            $pros->whereIn('id', $accessibleProIds);
        }
        $suggestedPros = $pros->orderBy('name_last')->orderBy('name_first')->get();
        return view('app/pro-suggest', compact('suggestedPros'));
    }

    public function proDisplayName(Request $request, Pro $pro) {
        return $pro ? $pro->displayName() : '';
    }

    public function unmappedSMS(Request $request, $filter = '')
    {
        $proID = $this->performer()->pro->id;
        if ($this->performer()->pro->pro_type === 'ADMIN') {
            $query = Client::where('id', '>', 0);
        } else {
            $query = Client::where(function ($q) use ($proID) {
                $q->where('mcp_pro_id', $proID)
                    ->orWhere('cm_pro_id', $proID)
                    ->orWhere('rmm_pro_id', $proID)
                    ->orWhere('rme_pro_id', $proID)
                    ->orWhereRaw('id IN (SELECT client_id FROM client_pro_access WHERE is_active AND pro_id = ?)', [$proID]);
            });
        }
        $patients = $query->orderBy('name_last', 'asc')->orderBy('name_first', 'asc')->get();
        $unmappedSMS = ClientSMS::where('client_id', null)->where('incoming_or_outgoing', 'INCOMING')->get();
        return view('app/unmapped-sms', compact('unmappedSMS', 'patients'));
    }

    public function newPatient(Request $request)
    {
        return view('app/new-patient');
    }

    public function mc(Request $request, $fragment = "")
    {
        $page = "/";
        if ($fragment) {
            $page = '/' . $fragment;
        }
        return view('app/mc', compact('page'));
    }

    public function blank(Request $request)
    {
        return view('app/blank');
    }

    public function noteTemplateSet(Request $request, $section, $template)
    {
        return view('app/patient/note/_template', [
            "sectionInternalName" => $section,
            "templateName" => $template
        ]);
    }

    public function noteExamTemplateSet(Request $request, $exam, $template)
    {
        return view('app/patient/note/_template-exam', [
            "exam" => $exam,
            "sectionInternalName" => 'exam-' . $exam . '-detail',
            "templateName" => $template
        ]);
    }

    public function logInAs(Request $request)
    {
        if($this->pro->pro_type != 'ADMIN'){
            return redirect()->to(route('dashboard'));
        }
        $pros =  Pro
            ::where('pro_type', '!=', 'ADMIN')
            ->orWhereNull('pro_type')
            ->orderBy('name_last', 'asc')
            ->orderBy('name_first', 'asc')
            ->get();
        return view('app/log-in-as', compact('pros'));
    }

    public function processLogInAs(Request $request)
    {

        $api = new Backend();

        try {
            $apiResponse = $api->post('session/proLogInAs', [
                'proUid' => $request->post('proUid')
            ],
            [
                'sessionKey'=>$this->performer()->session_key
            ]);

            $data = json_decode($apiResponse->getContents());

            if (!property_exists($data, 'success') || !$data->success) {
                return redirect()->to(route('log-in-as'))->with('message', $data->message)
                    ->withInput($request->input());
            }

            Cookie::queue('sessionKey', $data->data->sessionKey);

            return redirect('/mc');
        } catch (\Exception $e) {
            return redirect()->to(route('log-in-as'))
                ->with('message', 'Unable to process your request at the moment. Please try again later.')
                ->withInput($request->input());
        }
    }

    public function backToAdminPro(Request $request){
        $adminPerformerId = $this->performer->logged_in_as_pro_from_admin_pro_app_session_id;
        $adminPerformer = AppSession::where('id', $adminPerformerId)->first();
        $url = "/session/pro_log_in_with_session_key/".$adminPerformer->session_key;
        $api = new Backend();
        try {
            $apiResponse = $api->post($url, []);
            $data = json_decode($apiResponse->getContents());

            if (!property_exists($data, 'success') || !$data->success) {
                return redirect()->to(route('logout'));
            }


            Cookie::queue('sessionKey', $data->data->sessionKey);

            return redirect(route('dashboard'));

        } catch (\Exception $e) {
            return redirect(route('dashboard'));
        }
    }
}
