package DBIx::Fast::Schema;

use v5.38;
use Object::Pad;
use Carp 'carp';

class DBIx::Fast::Schema :isa(DBIx::Fast::Base) {
    field $tables :reader = {};

    method _set_tables ($t) { $tables = $t }

    method _load_tables_name () {
        my $driver = $self->dbix->dbd;
        my $sql    = '';

        if ( $driver eq 'MariaDB' || $driver eq 'mysql' ) {
            $sql = 'SHOW TABLES';
        }
        elsif ( $driver eq 'SQLite' ) {
            $sql = "SELECT name FROM sqlite_master WHERE type='table'";
        }
        elsif ( $driver eq 'Pg' ) {
            $sql = "SELECT tablename as name FROM pg_catalog.pg_tables WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'";
        }
        else {
            $self->dbix->Exception("Error driver - _load_tables_name");
        }

        # Hashref, not undef, so a database with zero tables leaves `tables`
        # honouring its documented contract (TableName gives a clean
        # "not exist" instead of an "undefined value as a HASH reference").
        my $table_list = {};

        for my $table ( @{ $self->dbix->array($sql) } ) {
            $table_list->{$table} = time();
        }

        $tables = $table_list;
    }

    method clear_cache () { $tables = {} }

    method get_indexes ($table) {
        $self->dbix->Exception("Table required") unless $table;

        my $driver = $self->dbix->dbd;
        my $qtable = $self->dbix->_quote_ident_path($table);
        my $sql    = '';

        if ( $driver eq 'MariaDB' || $driver eq 'mysql' ) {
            $sql = "SHOW INDEX FROM $qtable";
        }
        elsif ( $driver eq 'Pg' ) {
            $sql = qq{
                SELECT
                    i.relname as index_name,
                    a.attname as column_name,
                    ix.indisunique as is_unique
                FROM
                    pg_class t,
                    pg_class i,
                    pg_index ix,
                    pg_attribute a
                WHERE
                    t.oid = ix.indrelid
                    AND i.oid = ix.indexrelid
                    AND a.attrelid = t.oid
                    AND a.attnum = ANY(ix.indkey)
                    AND t.relkind = 'r'
                    AND t.relname = ?
            };
        }
        elsif ( $driver eq 'SQLite' ) {
            $sql = "SELECT * FROM sqlite_master WHERE type='index' AND tbl_name=?";
        }

        # Route through all() so the query is tracked (profiler / last_sql).
        my @bind = $driver =~ /^(Pg|SQLite)$/ ? ($table) : ();
        return $self->dbix->all($sql, @bind);
    }

    # Deprecated alias
    method get_indexs ($table) {
        state $warned;
        carp "DBIx::Fast::Schema::get_indexs is deprecated; use get_indexes"
            unless $warned++;
        return $self->get_indexes($table);
    }

    method primary_keys ($table) {
        $self->dbix->Exception("Table name required") unless $table;

        my $driver = $self->dbix->dbd;
        my $qtable = $self->dbix->_quote_ident_path($table);
        my $sql    = '';

        if ( $driver eq 'MariaDB' || $driver eq 'mysql' ) {
            $sql = "SHOW KEYS FROM $qtable WHERE Key_name = 'PRIMARY'";
        }
        elsif ( $driver eq 'Pg' ) {
            $sql = qq{
                SELECT a.attname
                FROM   pg_index i
                JOIN   pg_attribute a ON a.attrelid = i.indrelid
                                    AND a.attnum = ANY(i.indkey)
                WHERE  i.indrelid = ?::regclass
                AND    i.indisprimary
            };
        }
        elsif ( $driver eq 'SQLite' ) {
            $sql = "PRAGMA table_info($qtable)";
        }

        my @bind = $driver eq 'Pg' ? ($table) : ();
        my $rows = $self->dbix->all($sql, @bind);

        my @keys;
        if ( $driver eq 'SQLite' ) {
            @keys = map { $_->{name} } grep { $_->{pk} } @$rows;
        }
        else {
            @keys = map { $_->{Column_name} || $_->{attname} } @$rows;
        }

        return \@keys;
    }

    method table_info ($table = undef) {
        $self->dbix->Exception("Table name required") unless $table;

        my $driver = $self->dbix->dbd;
        my $qtable = $self->dbix->_quote_ident_path($table);
        my $sql    = '';

        if ( $driver eq 'MariaDB' || $driver eq 'mysql' ) {
            $sql = "SHOW CREATE TABLE $qtable";
        }
        elsif ( $driver eq 'Pg' ) {
            $sql = qq{SELECT column_name, data_type, character_maximum_length FROM information_schema.columns WHERE table_name = ?};
        }
        elsif ( $driver eq 'SQLite' ) {
            $sql = "PRAGMA table_info($qtable)";
        }

        my @bind;
        push @bind, $table if $driver eq 'Pg';

        return $self->dbix->all($sql, @bind);
    }

    method table_size ($table) {
        $self->dbix->Exception("Table name required") unless $table;

        my $driver = $self->dbix->dbd;
        my $qtable = $self->dbix->_quote_ident_path($table);
        my $sql    = '';
        my @params;

        if ( $driver eq 'MariaDB' || $driver eq 'mysql' ) {
            $sql = "SELECT table_rows as `rows`, data_length + index_length as size_bytes FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = ?";
            @params = ($table);
        }
        elsif ( $driver eq 'Pg' ) {
            $sql = q{SELECT reltuples::bigint as rows, pg_total_relation_size(?) as size_bytes FROM pg_class WHERE relname = ?};
            @params = ( $table, $table );
        }
        elsif ( $driver eq 'SQLite' ) {
            $sql = "SELECT count(*) as rows FROM $qtable";
        }

        return $self->dbix->hash($sql, @params);
    }
}

1;

__END__

=head1 NAME

DBIx::Fast::Schema - Schema introspection for DBIx::Fast

=head1 DESCRIPTION

Provides database schema introspection capabilities including listing tables,
retrieving index information, primary keys, column details, and table size
statistics. Supports MariaDB/MySQL, PostgreSQL, and SQLite drivers.

=head1 METHODS

=head2 tables

Reader accessor that returns the cached hash of table names.

=head2 clear_cache

Clears the internal table name cache.

=head2 get_indexes

    my $indexes = $schema->get_indexes($table);

Returns an arrayref of hashrefs describing all indexes on the given table.

=head2 get_indexs

B<Deprecated.> Misspelled alias for L</get_indexes>; kept for backward
compatibility and will be removed in a future release. Use C<get_indexes>
instead.

=head2 primary_keys

    my $keys = $schema->primary_keys($table);

Returns an arrayref of column names that form the primary key of the given table.

=head2 table_info

    my $info = $schema->table_info($table);

Returns an arrayref of hashrefs with column metadata (name, type, length) for
the given table.

=head2 table_size

    my $size = $schema->table_size($table);

Returns a hashref with C<rows> and C<size_bytes> for the given table.

=head1 AUTHOR

SeHarrys

=head1 LICENSE

This is free software under the Artistic License 2.0.

=cut
