<?php
namespace factory;

class Logs
{
    static public function add( $level, $source, $message, $context = null, $client_id = null )
    {
        global $mdb;

        $level = trim( (string) $level );
        if ( !in_array( $level, [ 'info', 'error', 'warning' ], true ) )
        {
            $level = 'info';
        }

        $context_json = null;
        if ( $context !== null )
        {
            $context_json = json_encode( $context, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE | JSON_PARTIAL_OUTPUT_ON_ERROR );
            if ( $context_json === false )
            {
                $context_json = json_encode( [ '_encoding_error' => json_last_error_msg(), '_type' => gettype( $context ) ] );
            }
        }

        $mdb -> insert( 'logs', [
            'level' => $level,
            'source' => trim( (string) $source ),
            'client_id' => $client_id !== null ? (int) $client_id : null,
            'message' => (string) $message,
            'context_json' => $context_json
        ] );

        return (int) $mdb -> id();
    }

    static public function get_data( $start, $length, $filters = [] )
    {
        global $mdb;

        $start = max( 0, (int) $start );
        $length = max( 1, min( 100, (int) $length ) );

        $where_parts = [];
        $params = [];

        if ( !empty( $filters['level'] ) )
        {
            $where_parts[] = 'l.level = :level';
            $params[':level'] = (string) $filters['level'];
        }

        if ( !empty( $filters['source'] ) )
        {
            $where_parts[] = 'l.source = :source';
            $params[':source'] = (string) $filters['source'];
        }

        if ( !empty( $filters['date_from'] ) )
        {
            $where_parts[] = 'l.date_add >= :date_from';
            $params[':date_from'] = (string) $filters['date_from'] . ' 00:00:00';
        }

        if ( !empty( $filters['date_to'] ) )
        {
            $where_parts[] = 'l.date_add <= :date_to';
            $params[':date_to'] = (string) $filters['date_to'] . ' 23:59:59';
        }

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

        $sql = "SELECT l.*, c.name AS client_name
                FROM logs AS l
                LEFT JOIN clients AS c ON c.id = l.client_id
                {$where_sql}
                ORDER BY l.date_add DESC
                LIMIT {$start}, {$length}";

        return $mdb -> query( $sql, $params ) -> fetchAll( \PDO::FETCH_ASSOC );
    }

    static public function get_records_total( $filters = [] )
    {
        global $mdb;

        $where_parts = [];
        $params = [];

        if ( !empty( $filters['level'] ) )
        {
            $where_parts[] = 'level = :level';
            $params[':level'] = (string) $filters['level'];
        }

        if ( !empty( $filters['source'] ) )
        {
            $where_parts[] = 'source = :source';
            $params[':source'] = (string) $filters['source'];
        }

        if ( !empty( $filters['date_from'] ) )
        {
            $where_parts[] = 'date_add >= :date_from';
            $params[':date_from'] = (string) $filters['date_from'] . ' 00:00:00';
        }

        if ( !empty( $filters['date_to'] ) )
        {
            $where_parts[] = 'date_add <= :date_to';
            $params[':date_to'] = (string) $filters['date_to'] . ' 23:59:59';
        }

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

        $row = $mdb -> query( "SELECT COUNT(*) AS cnt FROM logs {$where_sql}", $params ) -> fetch( \PDO::FETCH_ASSOC );
        return (int) ( $row['cnt'] ?? 0 );
    }

    static public function get_one( $id )
    {
        global $mdb;

        return $mdb -> query(
            "SELECT l.*, c.name AS client_name
             FROM logs AS l
             LEFT JOIN clients AS c ON c.id = l.client_id
             WHERE l.id = :id",
            [ ':id' => (int) $id ]
        ) -> fetch( \PDO::FETCH_ASSOC );
    }

    static public function cleanup_old( $days = 30 )
    {
        global $mdb;

        $days = max( 1, (int) $days );
        $mdb -> query( "DELETE FROM logs WHERE date_add < DATE_SUB( NOW(), INTERVAL {$days} DAY )" );
    }

    static public function get_sources()
    {
        global $mdb;

        $rows = $mdb -> query( "SELECT DISTINCT source FROM logs WHERE source != '' ORDER BY source ASC" ) -> fetchAll( \PDO::FETCH_COLUMN );
        return is_array( $rows ) ? $rows : [];
    }
}
