use strict;
use warnings;
use Test::More;
use Test::Exception;
use DBIx::Fast;
use Time::HiRes qw(time);

eval "use DBD::SQLite 1.74";
plan skip_all => "DBD::SQLite 1.74 required" if $@;

my $db = DBIx::Fast->new(
    dsn        => 'dbi:SQLite:dbname=:memory:',
    driver     => 'SQLite',
    RaiseError => 1,
    PrintError => 0,
);

use DBIx::Fast::Profiler;

subtest 'constructor' => sub {
    my $p = DBIx::Fast::Profiler->new(dbix => $db, auto_print => 0);
    ok($p, 'Profiler created');
    isa_ok($p, 'DBIx::Fast::Profiler');
    is($p->auto_print, 0, 'auto_print off');
    is($p->enabled, 1, 'enabled by default');
    is($p->slow_query, 1.0, 'slow_query default 1.0');
    is($p->max_queries, 1000, 'max_queries default 1000');
};

subtest 'add_query and queries buffer' => sub {
    my $p = DBIx::Fast::Profiler->new(dbix => $db, auto_print => 0);

    my $t0 = time();
    my $t1 = $t0 + 0.01;
    $p->add_query('SELECT 1', [], $t0, $t1);

    is(scalar @{$p->queries}, 1, 'One query recorded');

    my $q = $p->queries->[0];
    is($q->{sql}, 'SELECT 1', 'SQL recorded');
    is_deeply($q->{params}, [], 'Params recorded');
    ok($q->{duration} > 0, 'Duration calculated');
    ok($q->{time}, 'Timestamp recorded');
    ok(defined $q->{stack}, 'Stack trace recorded');
};

subtest 'add_query disabled' => sub {
    my $p = DBIx::Fast::Profiler->new(dbix => $db, auto_print => 0);
    $p->enabled(0);

    $p->add_query('SELECT 1', [], time(), time() + 0.01);
    is(scalar @{$p->queries}, 0, 'No queries when disabled');

    $p->enabled(1);
    $p->add_query('SELECT 1', [], time(), time() + 0.01);
    is(scalar @{$p->queries}, 1, 'Query recorded when re-enabled');
};

subtest 'buffer limit (max_queries)' => sub {
    my $p = DBIx::Fast::Profiler->new(dbix => $db, auto_print => 0, max_queries => 3);

    for my $i (1..5) {
        $p->add_query("SELECT $i", [], time(), time() + 0.001);
    }

    is(scalar @{$p->queries}, 3, 'Buffer limited to 3');
    is($p->queries->[0]{sql}, 'SELECT 3', 'Oldest queries evicted');
    is($p->queries->[2]{sql}, 'SELECT 5', 'Newest query present');
};

subtest 'clear' => sub {
    my $p = DBIx::Fast::Profiler->new(dbix => $db, auto_print => 0);
    $p->add_query('SELECT 1', [], time(), time() + 0.01);
    ok(scalar @{$p->queries} > 0, 'Has queries');

    $p->clear;
    is(scalar @{$p->queries}, 0, 'Cleared');
};

subtest 'get_stats with no queries' => sub {
    my $p = DBIx::Fast::Profiler->new(dbix => $db, auto_print => 0);
    my $stats = $p->get_stats;
    is($stats->{total_queries}, 0, 'No queries');
    is($stats->{total_time}, 0, 'No time');
};

subtest 'get_stats with queries' => sub {
    my $p = DBIx::Fast::Profiler->new(dbix => $db, auto_print => 0);

    my $t = time();
    $p->add_query('SELECT 1',     [], $t,       $t + 0.01);
    $p->add_query('INSERT INTO x', [], $t + 0.1, $t + 0.12);
    $p->add_query('UPDATE x',     [], $t + 0.2, $t + 0.25);
    $p->add_query('DELETE FROM x', [], $t + 0.3, $t + 0.32);

    my $stats = $p->get_stats;
    is($stats->{total_queries}, 4, '4 queries');
    ok($stats->{total_time} > 0, 'Total time > 0');
    ok($stats->{avg_time} > 0, 'Avg time > 0');
    ok(defined $stats->{min_time}, 'Min time defined');
    ok($stats->{max_time} > 0, 'Max time > 0');
    is($stats->{slow_queries}, 0, 'No slow queries');
    ok(exists $stats->{queries_per_second}, 'QPS exists');
    ok(exists $stats->{slow_query_percentage}, 'Slow % exists');

    # Query types
    ok(exists $stats->{query_types}{SELECT}, 'SELECT type');
    ok(exists $stats->{query_types}{INSERT}, 'INSERT type');
    ok(exists $stats->{query_types}{UPDATE}, 'UPDATE type');
    ok(exists $stats->{query_types}{DELETE}, 'DELETE type');
};

subtest 'get_stats with slow queries' => sub {
    my $p = DBIx::Fast::Profiler->new(
        dbix => $db, auto_print => 0, slow_query => 0.05
    );

    my $t = time();
    $p->add_query('SELECT 1', [], $t, $t + 0.01);         # fast
    $p->add_query('SELECT slow', [], $t + 0.1, $t + 0.2); # slow (0.1s > 0.05)

    my $stats = $p->get_stats;
    is($stats->{slow_queries}, 1, 'One slow query detected');
    ok($stats->{slow_query_percentage} > 0, 'Slow percentage > 0');
};

subtest 'get_detailed_stats' => sub {
    my $p = DBIx::Fast::Profiler->new(dbix => $db, auto_print => 0);

    my $t = time();
    $p->add_query('SELECT 1',          [], $t,       $t + 0.01);
    $p->add_query('SELECT 2',          [], $t + 0.1, $t + 0.12);
    $p->add_query('INSERT INTO x',     [], $t + 0.2, $t + 0.22);
    $p->add_query('UPDATE x SET a=1',  [], $t + 0.3, $t + 0.33);
    $p->add_query('DELETE FROM x',     [], $t + 0.4, $t + 0.42);
    $p->add_query('CREATE TABLE y',    [], $t + 0.5, $t + 0.51);
    $p->add_query('ALTER TABLE y',     [], $t + 0.6, $t + 0.61);
    $p->add_query('DROP TABLE y',      [], $t + 0.7, $t + 0.71);
    $p->add_query('PRAGMA table_info', [], $t + 0.8, $t + 0.81);

    my $stats = $p->get_detailed_stats;
    is($stats->{SELECT}{count}, 2, '2 SELECTs');
    is($stats->{INSERT}{count}, 1, '1 INSERT');
    is($stats->{UPDATE}{count}, 1, '1 UPDATE');
    is($stats->{DELETE}{count}, 1, '1 DELETE');
    is($stats->{CREATE}{count}, 1, '1 CREATE');
    is($stats->{ALTER}{count}, 1, '1 ALTER');
    is($stats->{DROP}{count}, 1, '1 DROP');
    is($stats->{OTHER}{count}, 1, '1 OTHER (PRAGMA)');

    # Check per-type stats structure
    for my $type (qw(SELECT INSERT UPDATE DELETE)) {
        ok(exists $stats->{$type}{total_time}, "$type has total_time");
        ok(exists $stats->{$type}{avg_time}, "$type has avg_time");
        ok(defined $stats->{$type}{min_time}, "$type has min_time");
        ok(exists $stats->{$type}{max_time}, "$type has max_time");
    }
};

subtest 'get_slow_queries' => sub {
    my $p = DBIx::Fast::Profiler->new(dbix => $db, auto_print => 0);

    my $t = time();
    $p->add_query('SELECT fast',  [], $t,       $t + 0.01);
    $p->add_query('SELECT slow1', [], $t + 0.1, $t + 0.3);   # 0.2s
    $p->add_query('SELECT slow2', [], $t + 0.4, $t + 0.8);   # 0.4s

    my $slow = $p->get_slow_queries(2);
    is(scalar @$slow, 2, 'Got 2 slowest');
    like($slow->[0]{sql}, qr/slow2/, 'Slowest first');
    like($slow->[1]{sql}, qr/slow1/, 'Second slowest');

    # Limit larger than total
    my $all = $p->get_slow_queries(100);
    is(scalar @$all, 3, 'Returns all when limit > count');
};

subtest 'print_query output' => sub {
    my $p = DBIx::Fast::Profiler->new(dbix => $db, auto_print => 0);

    my $t = time();
    $p->add_query('SELECT * FROM users WHERE id = ?', [42], $t, $t + 0.001);

    my $output = '';
    {
        local *STDOUT;
        open STDOUT, '>', \$output or die "Cannot redirect STDOUT: $!";
        $p->print_query($p->queries->[0]);
    }
    like($output, qr/Query at/, 'print_query outputs query header');
    like($output, qr/Duration/, 'print_query outputs duration');
    like($output, qr/SQL/, 'print_query outputs SQL');
    like($output, qr/Params.*42/, 'print_query outputs params');
};

subtest 'print_query slow query with stack trace' => sub {
    my $p = DBIx::Fast::Profiler->new(
        dbix => $db, auto_print => 0, slow_query => 0.001
    );

    my $t = time();
    $p->add_query('SELECT slow', [], $t, $t + 1.0);

    my $output = '';
    {
        local *STDOUT;
        open STDOUT, '>', \$output or die "Cannot redirect STDOUT: $!";
        $p->print_query($p->queries->[0]);
    }
    like($output, qr/Stack Trace/, 'Slow query shows stack trace');
};

subtest 'print_summary output' => sub {
    my $p = DBIx::Fast::Profiler->new(dbix => $db, auto_print => 0);

    my $t = time();
    $p->add_query('SELECT 1', [], $t, $t + 0.01);
    $p->add_query('INSERT INTO x', [], $t + 0.1, $t + 0.12);

    my $output = '';
    {
        local *STDOUT;
        open STDOUT, '>', \$output or die "Cannot redirect STDOUT: $!";
        $p->print_summary;
    }
    like($output, qr/Query Statistics Summary/, 'Summary header');
    like($output, qr/SELECT/, 'SELECT in summary');
    like($output, qr/INSERT/, 'INSERT in summary');
};

subtest 'print_stats output' => sub {
    my $p = DBIx::Fast::Profiler->new(dbix => $db, auto_print => 0);

    my $t = time();
    $p->add_query('SELECT 1', [], $t, $t + 0.01);

    my $output = '';
    {
        local *STDOUT;
        open STDOUT, '>', \$output or die "Cannot redirect STDOUT: $!";
        $p->print_stats;
    }
    like($output, qr/Query Statistics/, 'Stats header');
    like($output, qr/Total Queries/, 'Total queries line');
    like($output, qr/Queries\/second/, 'QPS line');
};

subtest 'auto_print triggers print_query' => sub {
    my $p = DBIx::Fast::Profiler->new(dbix => $db, auto_print => 1);

    my $output = '';
    {
        local *STDOUT;
        open STDOUT, '>', \$output or die "Cannot redirect STDOUT: $!";
        $p->add_query('SELECT auto', [], time(), time() + 0.001);
    }
    like($output, qr/SQL.*\n.*SELECT/s, 'auto_print prints on add_query');
};

done_testing;
