<?php

class GeofenceAreaModel
{
    public static function getCountryListWithPagination($db, $page = 1, $perPage = 10)
    {
        $offset = ($page - 1) * $perPage;
        if ($offset < 0) {
            $offset = 0;
        }

        $query_total = "SELECT count(*) FROM geofence_area_country";

        $query = "SELECT
                    id, country as country_code, latitude, longitude, location, radius
                FROM
                    geofence_area_country
                ORDER BY
                    id desc limit '" . pg_escape_string($perPage) . "' offset '" . pg_escape_string($offset) . "'";

        $rs = pg_query($db, $query);
        $rs_total = pg_query($db, $query_total);

        $data = pg_fetch_all($rs);

        return [
            'success' => true,
            'data' => !empty($data) ? $data : [],
            'total' => pg_fetch_result($rs_total, 'count'),
            'page' => $page,
        ];
    }

    public static function getCountryListWithoutPagination($db)
    {
        $query = "SELECT
                    id, country as country_code, latitude, longitude, location, radius
                FROM
                    geofence_area_country
                ORDER BY
                    id desc";

        $rs = pg_query($db, $query);
        $data = pg_fetch_all($rs);

        return [
            'success' => true,
            'data' => !empty($data) ? $data : []
        ];
    }

    public static function getCityListWithPagination($db, $page = 1, $perPage = 10)
    {
        $offset = ($page - 1) * $perPage;
        if ($offset < 0) {
            $offset = 0;
        }

        $query_total = "SELECT count(*) FROM geofence_area_city";

        $query = "SELECT
                    id, city, country as country_code, latitude, longitude, location, radius
                FROM
                    geofence_area_city
                ORDER BY
                    id desc limit '" . pg_escape_string($perPage) . "' offset '" . pg_escape_string($offset) . "'";

        $rs = pg_query($db, $query);
        $rs_total = pg_query($db, $query_total);

        $data = pg_fetch_all($rs);

        return [
            'success' => true,
            'data' => !empty($data) ? $data : [],
            'total' => pg_fetch_result($rs_total, 'count'),
            'page' => $page,
        ];
    }

    public static function getCityListWithoutPagination($db, $search = [])
    {
        $_self = new self;

        $query = "SELECT
                    id, city, country as country_code, 
                    latitude, longitude, location, 
                    radius
                  FROM
                    geofence_area_city";

        $whereQuery = '';
        $order = " ORDER BY id desc";

        if (!empty($search["country_code"])) {
            $condition = " LOWER(country) = '" . pg_escape_string(strtolower($search["country_code"])) . "'";
            $whereQuery = $_self->checkCondition($condition, $whereQuery);

            $query .= $whereQuery;

        }
        if (!empty($search["latitude"]) &&
            !empty($search["longitude"]) &&
            !empty($search["radius"])) {
            
            $earth_radius = 6371;
            $km_per_degree_lat = 111.2;
            $pi = 3.14/180;
            $latitude = $search["latitude"];
            $longitude = $search["longitude"];
            $distance_in_km = $search["radius"];

            $query =  " SELECT id
                              , city
                              , country as country_code
                              , latitude
                              , longitude
                              , location
                              , radius
                            FROM (
                              SELECT (
                                $earth_radius * acos(
                                    cos( radians( {$latitude}) )
                                  * cos( radians( latitude  ) )
                                  * cos( radians( longitude ) - radians( {$longitude} ) )
                                  + sin( radians( {$latitude}) )
                                  * sin( radians( latitude  ) ))
                                ) AS distance
                                , id
                                , city
                                , country
                                , latitude
                                , longitude
                                , location
                                , radius
                              FROM geofence_area_city
                              WHERE 
                                latitude BETWEEN " . ($latitude - ($distance_in_km / $km_per_degree_lat)) .
                              " AND " . ($latitude + ($distance_in_km / $km_per_degree_lat));

            $delta = round($distance_in_km  / ($km_per_degree_lat  * COS(deg2rad($longitude))), 10);

            // Bounding box for speed - latitude within range (longtitude to km not linear)
            if(cos($longitude * $pi) > 0) {

                $from =  $longitude - $delta;
                $to = $longitude + $delta;

                $query .= 
                  " AND longitude" .
                  " BETWEEN " . $from .
                  " AND " . $to;

            } else {

                $from =  $longitude + $delta;
                $to = $longitude - $delta;

                $query .= 
                  " AND longitude" .
                  " BETWEEN " . $from .
                  " AND " . $to;

            }

            // distance limit for circle
            $query .= " ) geofence_area_city
                        WHERE distance < " . $distance_in_km;
        }

        $query .= $order;

        $rs = pg_query($db, $query);
        $data = pg_fetch_all($rs);

        return [
            'success' => true,
            'data' => !empty($data) ? $data : []
        ];
    }

    public static function getAreaListWithPagination($db, $page = 1, $perPage = 10, $search = [])
    {
        $_self = new self;

        $offset = ($page - 1) * $perPage;
        if ($offset < 0) {
            $offset = 0;
        }

        $whereQuery = '';
        if (isset($search["city_id"])) {
            $condition = " geofence_area_city.id = '" . pg_escape_string($search["city_id"]) . "'";
            $whereQuery = $_self->checkCondition($condition, $whereQuery);
        }

        $query_total = "SELECT count(*)
                        FROM
                            geofence_area
                                JOIN geofence_area_city ON geofence_area_city.id = geofence_area.city_id " . $whereQuery;

        $query = "SELECT
                    geofence_area.id, geofence_area.name, geofence_area_city.city, geofence_area.country as country_code, geofence_area.latitude, geofence_area.longitude,
                    geofence_area.location, geofence_area.type, geofence_area.boundaries
                FROM
                    geofence_area
                        JOIN geofence_area_city ON geofence_area_city.id = geofence_area.city_id
                " . $whereQuery . "
                ORDER BY
                    id desc limit '" . pg_escape_string($perPage) . "' offset '" . pg_escape_string($offset) . "'";

        $rs = pg_query($db, $query);
        $rs_total = pg_query($db, $query_total);

        $data = pg_fetch_all($rs);

        return [
            'success' => true,
            'data' => !empty($data) ? $data : [],
            'total' => pg_fetch_result($rs_total, 'count'),
            'page' => $page,
        ];
    }

    public static function getAreaListWithoutPagination($db, $search = [])
    {
        $_self = new self;
        $whereQuery = '';

        if (!empty($search["city_id"])) {
            $condition = " geofence_area_city.id = '" . pg_escape_string($search["city_id"]) . "'";
            $whereQuery = $_self->checkCondition($condition, $whereQuery);
        }

        if (!empty($search["country_code"])) {
            $condition = " LOWER(geofence_area.country) = '" . pg_escape_string(strtolower($search["country_code"])) . "'";
            $whereQuery = $_self->checkCondition($condition, $whereQuery);
        }

        $query = "SELECT
                    geofence_area.id, geofence_area.name, geofence_area_city.city, geofence_area.country as country_code, geofence_area.latitude, geofence_area.longitude,
                    geofence_area.location, geofence_area.type, geofence_area.boundaries
                FROM
                    geofence_area
                        JOIN geofence_area_city ON geofence_area_city.id = geofence_area.city_id
                " . $whereQuery . "
                ORDER BY
                    id desc";

        $rs = pg_query($db, $query);
        $data = pg_fetch_all($rs);

        return [
            'success' => true,
            'data' => !empty($data) ? $data : []
        ];
    }

    public static function getTripListBetweenArea($db, $startAreaId, $endAreaId, $search = [])
    {
        $_self = new self;
        $whereQuery = '';
        if (!empty($search["transport_provider_id"])) {
            $condition = " parsedemail_item.transport_provider_id = '" . pg_escape_string($search["transport_provider_id"]) . "'";
            $whereQuery = $_self->checkCondition($condition, $whereQuery);
        }

        // 1) Define geofenced areas, get list of areas
        $startArea = $_self->getArea($db, $startAreaId);
        $endArea = $_self->getArea($db, $endAreaId);

        if (!$startArea || !$endArea) {
            return [
                'success' => false,
                'message' => 'Can not find area.'
            ];
        }

        // 2. Select trips where "from" falls into origin area and "to" falls into destination area
        $startBoundaries = json_decode($startArea['boundaries'], true);
        $endBoundaries = json_decode($endArea['boundaries'], true);
        if ($startArea['type'] === 'radius') {
            if (isset($startBoundaries['radius'])) {
                $radius = $startBoundaries['radius'];
                $condition = " ST_DWithin('" . $startArea['location'] . "', startAddr.geometry, " . $radius . ") ";
                $whereQuery = $_self->checkCondition($condition, $whereQuery);
            } else {
                return [
                    'success' => false,
                    'message' => 'Missing radius in boundaries field.'
                ];
            }
        } else if ($startArea['type'] === 'postal') {
            if (is_array($startBoundaries) && array_key_exists("postal_code",$startBoundaries) && is_array($startBoundaries["postal_code"]) && count($startBoundaries["postal_code"]) > 0) {
                $condition = "";
                foreach ($startBoundaries['postal_code'] as $code) {
                    if ($condition != "") {
                        $condition .= " OR ";
                    }
                    $condition .= " LEFT(startAddr.postal, " . strlen($code) . ") = '${code}' ";
                }

                $condition = "(${condition})";
                $whereQuery = $_self->checkCondition($condition, $whereQuery);
            } else {
                return [
                    'success' => false,
                    'message' => 'Missing postal_code in boundaries field.'
                ];
            }
        } else if ($startArea['type'] === 'polygon') {
            if (isset($startBoundaries['polygon'])) {
                $polygon = $startBoundaries['polygon'];

                $polygonCoords = [];
                foreach ($polygon as $coord) {
                    $polygonCoords[] = "$coord[0] $coord[1]";
                }
                $polygonCoordsString = implode(",", $polygonCoords);

                $condition = " ST_Contains(ST_GeomFromText('POLYGON((${polygonCoordsString}))', 4326), startAddr.geometry::geometry) ";
                $whereQuery = $_self->checkCondition($condition, $whereQuery);
            } else {
                return [
                    'success' => false,
                    'message' => 'Missing polygon in boundaries field.'
                ];
            }
        }

        if ($endArea['type'] === 'radius') {
            if (isset($endBoundaries['radius'])) {
                $radius = $endBoundaries['radius'];
                $condition = " ST_DWithin('" . $endArea['location'] . "', endAddr.geometry, " . $radius . ") ";
                $whereQuery = $_self->checkCondition($condition, $whereQuery);
            } else {
                return [
                    'success' => false,
                    'message' => 'Missing radius in boundaries field.'
                ];
            }
        } else if ($endArea['type'] === 'postal') {
            if (is_array($endBoundaries) && array_key_exists("postal_code",$endBoundaries) && is_array($endBoundaries["postal_code"]) && count($endBoundaries["postal_code"]) > 0) {
                $condition = "";
                foreach ($endBoundaries['postal_code'] as $code) {
                    if ($condition != "") {
                        $condition .= " OR ";
                    }
                    $condition .= " LEFT(endAddr.postal, " . strlen($code) . ") = '${code}' ";
                }

                $condition = "(${condition})";
                $whereQuery = $_self->checkCondition($condition, $whereQuery);
            } else {
                return [
                    'success' => false,
                    'message' => 'Missing postal_code in boundaries field.'
                ];
            }
        } else if ($endArea['type'] === 'polygon') {
            if (isset($endBoundaries['polygon'])) {
                $polygon = $endBoundaries['polygon'];

                $polygonCoords = [];
                foreach ($polygon as $coord) {
                    $polygonCoords[] = "$coord[0] $coord[1]";
                }
                $polygonCoordsString = implode(",", $polygonCoords);

                $condition = " ST_Contains(ST_GeomFromText('POLYGON((${polygonCoordsString}))', 4326), endAddr.geometry::geometry) ";
                $whereQuery = $_self->checkCondition($condition, $whereQuery);
            } else {
                return [
                    'success' => false,
                    'message' => 'Missing polygon in boundaries field.'
                ];
            }
        }

        // only get trips within last year
        $numberOfDays = 365;
        $condition = " parsedemail_item.travel_date BETWEEN NOW() - INTERVAL '" . $numberOfDays . " days' AND NOW() ";
        $whereQuery = $_self->checkCondition($condition, $whereQuery);
        
        $tripQuery = 
            "SELECT
                parsedemail_item.id as trip_id, parsedemail_item.transport_provider_id, startAddr.address as start_address, endAddr.address as end_address
            FROM
                parsedemail_item
                JOIN address AS startAddr ON (startAddr.id = parsedemail_item.location_start_id AND transport_provider_id IS NOT NULL
                                                AND LOWER(startAddr.country) = '" . strtolower($startArea['country_code']) . "')
                JOIN address AS endAddr ON (endAddr.id = parsedemail_item.location_end_id
                                                AND LOWER(endAddr.country) = '" . strtolower($endArea['country_code']) . "')
            " . $whereQuery;

        $data = pg_fetch_all(pg_query($db, $tripQuery));

        $result = [
            'from_area' => $startArea,
            'to_area' => $endArea,
            'trip_list' => $data ?: []
        ];

        return [
            'success' => true,
            'data' => $result ?: []
        ];
    }

    public static function priceComparison($db, $startAreaId, $endAreaId)
    {
        $_self = new self;

        // 1) Define geofenced areas, get list of areas
        $startArea = $_self->getArea($db, $startAreaId);
        $endArea = $_self->getArea($db, $endAreaId);

        if (!$startArea || !$endArea) {
            return [
                'success' => false,
                'message' => 'Can not find area.'
            ];
        }

        $tripQuery = "SELECT ROUND(SUM(average_total)/SUM(average_count), 2) as avg_price, transport_provider_id";
        $tripQuery.= " FROM geofence_area_average_quotes ";
        $tripQuery.= " WHERE area_start_id=${startAreaId} AND area_end_id=${endAreaId}";
        $tripQuery.= " GROUP BY transport_provider_id";

        $data = pg_fetch_all(pg_query($db, $tripQuery));
        error_log($tripQuery);

        $result = [
            'from_area' => $startArea,
            'to_area' => $endArea,
            'price_comparison' => $data ?: []
        ];

        return $result;
    }

    public static function priceComparisonOld($db, $startAreaId, $endAreaId)
    {
        $_self = new self;

        // 1) Define geofenced areas, get list of areas
        $startArea = $_self->getArea($db, $startAreaId);
        $endArea = $_self->getArea($db, $endAreaId);

        if (!$startArea || !$endArea) {
            return [
                'success' => false,
                'message' => 'Can not find area.'
            ];
        }

        // 2. Select trips where "from" falls into origin area and "to" falls into destination area
        $whereQuery = '';
        $startBoundaries = json_decode($startArea['boundaries'], true);
        $endBoundaries = json_decode($endArea['boundaries'], true);
        if ($startArea['type'] === 'radius') {
            if (isset($startBoundaries['radius'])) {
                $radius = $startBoundaries['radius'];
                $condition = " ST_DWithin('" . $startArea['location'] . "', startAddr.geometry, " . $radius . ") ";
                $whereQuery = $_self->checkCondition($condition, $whereQuery);
            } else {
                return [
                    'success' => false,
                    'message' => 'Missing radius in boundaries field.'
                ];
            }
        } else if ($startArea['type'] === 'postal') {
            if (is_array($startBoundaries) && array_key_exists("postal_code",$startBoundaries) && is_array($startBoundaries["postal_code"]) && count($startBoundaries["postal_code"]) > 0) {
                $condition = "";
                foreach ($startBoundaries['postal_code'] as $code) {
                    if ($condition != "") {
                        $condition .= " OR ";
                    }
                    $condition .= " LEFT(startAddr.postal, " . strlen($code) . ") = '${code}' ";
                }

                $condition = "(${condition})";
                $whereQuery = $_self->checkCondition($condition, $whereQuery);
            } else {
                return [
                    'success' => false,
                    'message' => 'Missing postal_code in boundaries field.'
                ];
            }
        } else if ($startArea['type'] === 'polygon') {
            if (isset($startBoundaries['polygon'])) {
                $polygon = $startBoundaries['polygon'];

                $polygonCoords = [];
                foreach ($polygon as $coord) {
                    $polygonCoords[] = "$coord[0] $coord[1]";
                }
                $polygonCoordsString = implode(",", $polygonCoords);

                $condition = " ST_Contains(ST_GeomFromText('POLYGON((${polygonCoordsString}))', 4326), startAddr.geometry::geometry) ";
                $whereQuery = $_self->checkCondition($condition, $whereQuery);
            } else {
                return [
                    'success' => false,
                    'message' => 'Missing polygon in boundaries field.'
                ];
            }
        }

        if ($endArea['type'] === 'radius') {
            if (isset($endBoundaries['radius'])) {
                $radius = $endBoundaries['radius'];
                $condition = " ST_DWithin('" . $endArea['location'] . "', endAddr.geometry, " . $radius . ") ";
                $whereQuery = $_self->checkCondition($condition, $whereQuery);
            } else {
                return [
                    'success' => false,
                    'message' => 'Missing radius in boundaries field.'
                ];
            }
        } else if ($endArea['type'] === 'postal') {
            if (is_array($endBoundaries) && array_key_exists("postal_code",$endBoundaries) && is_array($endBoundaries["postal_code"]) && count($endBoundaries["postal_code"]) > 0) {
                $condition = "";
                foreach ($endBoundaries['postal_code'] as $code) {
                    if ($condition != "") {
                        $condition .= " OR ";
                    }
                    $condition .= " LEFT(endAddr.postal, " . strlen($code) . ") = '${code}' ";
                }

                $condition = "(${condition})";
                $whereQuery = $_self->checkCondition($condition, $whereQuery);
            } else {
                return [
                    'success' => false,
                    'message' => 'Missing postal_code in boundaries field.'
                ];
            }
        } else if ($endArea['type'] === 'polygon') {
            if (isset($endBoundaries['polygon'])) {
                $polygon = $endBoundaries['polygon'];

                $polygonCoords = [];
                foreach ($polygon as $coord) {
                    $polygonCoords[] = "$coord[0] $coord[1]";
                }
                $polygonCoordsString = implode(",", $polygonCoords);

                $condition = " ST_Contains(ST_GeomFromText('POLYGON((${polygonCoordsString}))', 4326), endAddr.geometry::geometry) ";
                $whereQuery = $_self->checkCondition($condition, $whereQuery);
            } else {
                return [
                    'success' => false,
                    'message' => 'Missing polygon in boundaries field.'
                ];
            }
        }

        // 3. Filter pricing by transport_provider_id and calculate average within last year

        // only get trips within last year
        $numberOfDays = 365;
        $condition = " parsedemail_item.travel_date BETWEEN NOW() - INTERVAL '" . $numberOfDays . " days' AND NOW() ";
        $whereQuery = $_self->checkCondition($condition, $whereQuery);

        $tripQuery = 
            "SELECT
                ROUND(AVG(parsedemail_item.cost), 2) as avg_price, parsedemail_item.transport_provider_id
            FROM
                parsedemail_item
                JOIN address AS startAddr ON (startAddr.id = parsedemail_item.location_start_id AND transport_provider_id IS NOT NULL 
                                            AND LOWER(startAddr.country) = '" . strtolower($startArea['country_code']) . "')
                JOIN address AS endAddr ON (endAddr.id = parsedemail_item.location_end_id 
                                            AND LOWER(endAddr.country) = '" . strtolower($endArea['country_code']) . "')
            " . $whereQuery . "
            GROUP BY parsedemail_item.transport_provider_id
            ";

            

        $data = pg_fetch_all(pg_query($db, $tripQuery));

        $result = [
            'from_area' => $startArea,
            'to_area' => $endArea,
            'price_comparison' => $data ?: []
        ];

        return $result;
    }

    private function checkCondition($condition, $query)
    {
        if (strpos($query, 'WHERE') === false) {
            $query .= " WHERE $condition";
        } else {
            $query .= " AND $condition";
        }
        return $query;
    }

    public static function getArea($db, $id)
    {
        $query = "SELECT
                    geofence_area.id, geofence_area.name, geofence_area.country as country_code, geofence_area.latitude, geofence_area.longitude,
                    geofence_area.location, geofence_area.type, geofence_area.boundaries
                FROM
                    geofence_area
                WHERE 
                    geofence_area.id = $id
                LIMIT 1
        ";

        $rs = pg_query($db, $query);
        $data = pg_fetch_assoc($rs);

        return $data;
    }
}
