<?php

namespace WtbFirewall\Firewall;

// Prevent direct access.
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Denne klassen brukes til å håndtere loggen for firewall.
 */
class Log {

    public function __construct() {
        
    }

    /**
     * Henter ut innhold fra loggen sortert på dato.
     *      
     * @param int $offset Hvilken log entry vi starter på.
     * @param int $limit Anall log entries som skal hentes ut.
     * @param int $eventType Dersom satt vil vi kun returnere log entries av gitt type.
     * @param int $ipv4 Eventuell IPv4 vi skal filtrere på.
     * @param string $ipv6 Eventuell IPv6 vi skal filtrere på.
     * 
     * @return type Array som inneholder alle log entries.
     */
    public function getLogEntries(int $offset, int $limit, ?int $eventType = null, ?int $ipv4 = null, ?string $ipv6 = null) {

        global $wpdb;

        $tableName = $wpdb->prefix . 'wtb_firewall_log';

        $where = '';
        $params = array();

        //Lag WHERE delen basert på hvilkene parametere som er satt.
        if ($eventType !== null || $ipv4 !== null || $ipv6 !== null) {
            $where = 'WHERE';

            if ($eventType !== null) {
                $where .= ' event_type = %d';
                $params[] = (int) $eventType;
            }

            if ($ipv4 !== null) {
                if ($eventType !== null) {
                    $where .= ' AND';
                }

                $where .= ' ipv4 = %d';
                $params[] = (int) $ipv4;
            }

            if ($ipv6 !== null) {
                if ($eventType !== null || $ipv4 !== null) {
                    $where .= ' AND';
                }

                $where .= ' ipv6 = %s';
                $params[] = $ipv6;
            }
        }

        $params[] = $limit;
        $params[] = $offset;

        $preparedSql = $wpdb->prepare("SELECT * FROM {$tableName} {$where} ORDER BY event_time DESC LIMIT %d OFFSET %d", ...$params);

        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Prepared with $wpdb->prepare() above.
        return $wpdb->get_results($preparedSql, ARRAY_A);
    }

    /**
     * Returnerer totalt antall log entries.
     * 
     * @param int $eventType Dersom satt returneres totalt antall av gitt type.
     * @param int $ipv4 Dersom satt returneres totalt antall for oppgitt IPv4 addresse.
     * @param string $ipv6 Dersom satt returneres totalt antall for oppgitt IPv6 addresse.    
     */
    public function getLogCount(?int $eventType, ?int $ipv4, ?string $ipv6): int {
        global $wpdb;

        $tableName = $wpdb->prefix . 'wtb_firewall_log';

        $whereParts = [];
        $params = [];

        if ($eventType !== null) {
            $whereParts[] = 'event_type = %d';
            $params[] = (int) $eventType;
        }

        if ($ipv4 !== null) {
            $whereParts[] = 'ipv4 = %d';
            $params[] = (int) $ipv4;
        }

        if ($ipv6 !== null) {
            $whereParts[] = 'ipv6 = %s';
            $params[] = $ipv6;
        }

        $where = '';
        if (!empty($whereParts)) {
            $where = 'WHERE ' . implode(' AND ', $whereParts);
        }

        $sql = "SELECT COUNT(*) FROM {$tableName} {$where}";

        if (!empty($params)) {
            $sql = $wpdb->prepare("SELECT COUNT(*) FROM {$tableName} {$where}", ...$params);
        }

        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Prepared with $wpdb->prepare() above.
        return (int) $wpdb->get_var($sql);
    }

    /**
     * Sletter alle log entries som er eldre enn oppgitt antall dager.
     * 
     * @param int $days Antall dager. Log entries eldre enn dette blir slettet.
     */
    public function deleteLogEntries(int $days) {
        if ($days < 1) {
            return;
        }

        global $wpdb;

        $table = $wpdb->prefix . 'wtb_firewall_log';

        $cutoff = gmdate('Y-m-d H:i:s', time() - ($days * DAY_IN_SECONDS));

        $wpdb->query($wpdb->prepare("DELETE FROM {$table} WHERE event_time < %s", $cutoff));
    }

    /**
     * Oppretter en ny log entry.
     * 
     * @param string $ip IP dette gjelder for.
     * @param \WtbFirewall\Firewall\EventType $eventType Hvilken type event dette gjelder
     * @param string $note Eventuell informasjon/text relatert til hendelsen.
     * @parma string $time Tidspunkt for hendelsen. Gitt som DATETIME format.
     */
    public function saveLogEntry(string $ip, \WtbFirewall\Firewall\EventType $eventType, string $note, string $time) {

        $ipv4 = null;
        $ipv6 = null;

        if (\WtbFirewall\Helpers\IpHelper::isValidIpv4($ip)) {
            $ipv4 = ip2long($ip);
        } else if (\WtbFirewall\Helpers\IpHelper::isValidIpv6($ip)) {
            $ipv6 = inet_pton($ip);
        }

        global $wpdb;
        $table = $wpdb->prefix . 'wtb_firewall_log';

        if ($ipv4 !== null) {
            $data = [
                'event_time' => $time,
                'event_type' => (int) $eventType->value,
                'ipv4' => (int) $ipv4,
                'details' => $note,
            ];

            $fmt = [
                '%s', // event_time
                '%d', // event_type
                '%d', // ipv4           
                '%s', // details            
            ];
        }

        if ($ipv6 !== null) {
            $data = [
                'event_time' => $time,
                'event_type' => (int) $eventType->value,
                'ipv6' => $ipv6,
                'details' => $note,
            ];

            $fmt = [
                '%s', // event_time
                '%d', // event_type
                '%s', // ipv6
                '%s', // details            
            ];
        }

        $wpdb->insert($table, $data, $fmt);
    }

    /**
     * Importerer logfil laget av prepend firewall scriptet.
     * 
     * @param string $logFilePath Logfilen.
     */
    public function importLogFile(string $logFilePath) {
        global $wp_filesystem;

        if (!$wp_filesystem) {
            require_once ABSPATH . 'wp-admin/includes/file.php';

            if (!WP_Filesystem()) {
                return;
            }
        }

        if (!$wp_filesystem->exists($logFilePath) || !$wp_filesystem->is_file($logFilePath)) {
            return;
        }

        $tempFilePath = $logFilePath . '.processing.' . wp_generate_password(12, false, false);

        if (!$wp_filesystem->move($logFilePath, $tempFilePath, true)) {
            return;
        }

        $content = $wp_filesystem->get_contents($tempFilePath);

        if (false === $content) {
            $wp_filesystem->delete($tempFilePath);
            return;
        }

        $firewall = new \WtbFirewall\Firewall\Firewall();

        try {
            $lines = preg_split("/\r\n|\n|\r/", $content);

            foreach ($lines as $line) {
                $line = trim($line);

                if ($line === '') {
                    continue;
                }

                $json = json_decode($line, true);

                if (!is_array($json)) {
                    continue;
                }

                if (empty($json['type'])) {
                    continue;
                }

                switch ($json['type']) {
                    case 'blocked':
                        if (empty($json['ip']) || empty($json['time']) || !isset($json['rule_id'])) {
                            break;
                        }

                        $this->saveLogEntry($json['ip'], \WtbFirewall\Firewall\EventType::CONNECTION_BLOCKED, '', $json['time']);

                        $rule = $firewall->getRule($json['rule_id']);

                        if ($rule === null) {
                            break;
                        }

                        $rule['hits'] = ($rule['hits'] ?? 0) + 1;
                        $rule['date_last_hit'] = $json['time'];

                        $firewall->updateRule($rule);
                        break;

                    default:
                        break;
                }
            }
        } finally {
            if ($wp_filesystem->exists($tempFilePath)) {
                $wp_filesystem->delete($tempFilePath);
            }
        }
    }
}
