package Bif::DB::Plugin::ChangeIDv1;
use strict;
use warnings;
use DBIx::ThinSQL qw/case qv/;

our $VERSION = '0.1.5_3';

sub Bif::DB::db::xprepare_changeset_v1 {
    my $self = shift;

    return $self->xprepare(
        @_,

        # change
        select => [
            qv('change'),    # 0
            'c.id',          # 1
            'p.id',          # 2
            'i.id',          # 3
            'c.mtime',       # 4
            'c.mtimetz',     # 5
            'c.author',      # 6
            'c.email',       # 7
            'c.lang',        # 8
            'c.message',     # 9
            'c.ucount',      # 10
            'c.itime',       # 11
            'c.delta_id',    # 12
        ],
        from       => 'src',
        inner_join => 'changes c',
        on         => 'c.id = src.id',
        left_join  => 'topics i',
        on         => 'i.id = c.identity_id',
        left_join  => 'changes p',
        on         => 'p.id = c.parent_id',

        # change_deltas
        union_all_select => [
            qv('change_delta'),    # 0
            't1.id',               # 1
            't2.id',               # 2
            'cd.action_format',    # 3
            'src.id',              # 4
            'cd.new',              # 5
            6, 7, 8, 9, 10, 11,
            'cd.id AS delta_id',    # 12
        ],
        from       => 'src',
        inner_join => 'change_deltas cd',
        on         => 'cd.change_id = src.id',
        left_join  => 'topics t1',
        on         => 't1.id = cd.action_topic_id_1',
        left_join  => 'topics t2',
        on         => 't2.id = cd.action_topic_id_2',

        # entities
        union_all_select => [
            case (
                when => 'ed.new',
                then => qv('entity'),
                else => qv('entity_delta'),
            ),            # 0
            't.id',       # 1
            'ed.name',    # 2
            't2.id',      # 3
            't3.id',      # 4
            'src.id',     # 5
            6, 7, 8, 9, 10, 11,
            'ed.id AS delta_id',    # 12
        ],
        from       => 'src',
        inner_join => 'entity_deltas ed',
        on         => 'ed.change_id = src.id',
        inner_join => 'topics t',
        on         => 't.id = ed.entity_id',
        left_join  => 'topics t2',
        on         => 't2.id = ed.contact_id',
        left_join  => 'topics t3',
        on         => 't3.id = ed.default_contact_method_id',

        # entity_contact_methods
        union_all_select => [
            case (
                when => 'ecmd.new',
                then => qv('entity_contact_method'),
                else => qv('entity_contact_method_delta'),
            ),                # 0
            't.id',           # 1
            'ecmd.method',    # 2
            'ecmd.mvalue',    # 3
            't2.id',          # 4
            'src.id',         # 5
            6, 7, 8, 9, 10, 11,
            'ecmd.id AS delta_id',    # 12
        ],
        from       => 'src',
        inner_join => 'entity_contact_method_deltas ecmd',
        on         => 'ecmd.change_id = src.id',
        inner_join => 'entity_contact_methods ecm',
        on         => 'ecm.id = ecmd.entity_contact_method_id',
        inner_join => 'topics t',
        on         => 't.id = ecm.id',
        inner_join => 'topics t2',
        on         => 't2.id = ecm.entity_id',

        # hubs
        union_all_select => [
            case (
                when => 'hd.new',
                then => qv('hub'),
                else => qv('hub_delta'),
            ),            # 0
            't.id',       # 1
            'hd.name',    # 2
            'src.id',     # 3
            4,  5, 6, 7, 8, 9,
            10, 11,
            'hd.id AS delta_id',    # 12
        ],
        from       => 'src',
        inner_join => 'hub_deltas hd',
        on         => 'hd.change_id = src.id',
        inner_join => 'topics t',
        on         => 't.id = hd.hub_id',

        # hub_repos
        union_all_select => [
            case (
                when => 'hrd.new',
                then => qv('hub_repo'),
                else => qv('hub_repo_delta'),
            ),                 # 0
            't.id',            # 1
            'h.id',            # 2
            'hrd.location',    # 3
            'src.id',          # 4
            5, 6, 7, 8, 9, 10,
            11,
            'hrd.id AS delta_id',    # 12
        ],
        from       => 'src',
        inner_join => 'hub_repo_deltas hrd',
        on         => 'hrd.change_id = src.id',
        inner_join => 'topics t',
        on         => 't.id = hrd.hub_repo_id',
        inner_join => 'hub_repos hr',
        on         => 'hr.id = hrd.hub_repo_id',
        left_join  => 'topics h',
        on         => 'h.id = hr.hub_id',

        # identities
        union_all_select => [
            case (
                when => 'id.new',
                then => qv('identity'),
                else => qv('identity_delta'),
            ),                 # 0
            't.id',            # 1
            'id.shortname',    # 2
            'src.id',          # 3
            4,  5, 6, 7, 8, 9,
            10, 11,
            'id.id AS delta_id',    # 12
        ],
        from       => 'src',
        inner_join => 'identity_deltas id',
        on         => 'id.change_id = src.id',
        inner_join => 'changes c',
        on         => 'c.id = id.change_id',
        inner_join => 'topics t',
        on         => 't.id = id.identity_id',

        # issue_status
        union_all_select => [
            case (
                when => 'isd.new',
                then => qv('issue_status'),
                else => qv('issue_status_delta'),
            ),               # 0
            't.id',          # 1
            'p.id',          # 2
            'isd.status',    # 3
            'isd.def',       # 4
            'isd.rank',      # 5
            'src.id',        # 6
            7, 8, 9, 10, 11,
            'isd.id AS delta_id',    # 12
        ],
        from       => 'src',
        inner_join => 'issue_status_deltas isd',
        on         => 'isd.change_id = src.id',
        left_join  => 'issue_status ist',
        on         => 'ist.id = isd.issue_status_id',
        inner_join => 'topics t',
        on         => 't.id = ist.id',
        inner_join => 'topics p',
        on         => 'p.id = ist.project_id',

        # issues
        union_all_select => [
            case (
                when => 'id.new',
                then => qv('issue'),
                else => qv('issue_delta'),
            ),                        # 0
            'i.id',                   # 1
            'ist.id',                 # 2
            'id.title',               # 3
            'src.id AS change_id',    # 4
            'i.src_id',               # 5
            6, 7, 8, 9, 10, 11,
            'id.id AS delta_id',      # 12
        ],
        from       => 'src',
        inner_join => 'issue_deltas id',
        on         => 'id.change_id = src.id',
        inner_join => 'issues i',
        on         => 'i.id = id.issue_id',
        inner_join => 'topics t',
        on         => 't.id = id.issue_id',
        left_join  => 'topics ist',
        on         => 'ist.id = id.issue_status_id',

        # project_status
        union_all_select => [
            case (
                when => 'psd.new',
                then => qv('project_status'),
                else => qv('project_status_delta'),
            ),               # 0
            't.id',          # 1
            'p.id',          # 2
            'psd.status',    # 3
            'psd.rank',      # 4
            'src.id',        # 5
            6, 7, 8, 9, 10, 11,
            'psd.id AS delta_id',    # 12
        ],
        from       => 'src',
        inner_join => 'project_status_deltas psd',
        on         => 'psd.change_id = src.id',
        left_join  => 'project_status pst',
        on         => 'pst.id = psd.project_status_id',
        inner_join => 'topics t',
        on         => 't.id = pst.id',
        inner_join => 'topics p',
        on         => 'p.id = pst.project_id',

        # projects
        union_all_select => [
            case (
                when => 'pd.new',
                then => qv('project'),
                else => qv('project_delta'),
            ),                   # 0
            'p.id',              # 1
            'par.id',            # 2
            'pd.name',           # 3
            'pd.title',          # 4
            's.id',              # 5
            'h.id AS hub_id',    # 6
            'src.id',            # 7
            'p.local',           # 8
            9, 10, 11,
            'pd.id AS delta_id',    # 12
        ],
        from       => 'src',
        inner_join => 'project_deltas pd',
        on         => 'pd.change_id = src.id',
        inner_join => 'projects p',
        on         => 'p.id = pd.project_id',
        left_join  => 'topics h',
        on         => 'h.id = pd.hub_id',
        left_join  => 'topics par',
        on         => 'par.id = pd.parent_id',
        left_join  => 'topics s',
        on         => 's.id = pd.project_status_id',

        # task_status
        union_all_select => [
            case (
                when => 'tsd.new',
                then => qv('task_status'),
                else => qv('task_status_delta'),
            ),               # 0
            't.id',          # 1
            'p.id',          # 2
            'tsd.status',    # 3
            'tsd.def',       # 4
            'tsd.rank',      # 5
            'src.id',        # 6
            7, 8, 9, 10, 11,
            'tsd.id AS delta_id',    # 12
        ],
        from       => 'src',
        inner_join => 'task_status_deltas tsd',
        on         => 'tsd.change_id = src.id',
        left_join  => 'task_status ts',
        on         => 'ts.id = tsd.task_status_id',
        inner_join => 'topics t',
        on         => 't.id = ts.id',
        inner_join => 'topics p',
        on         => 'p.id = ts.project_id',

        # tasks
        union_all_select => [
            case (
                when => 'td.new',
                then => qv('task'),
                else => qv('task_delta'),
            ),             # 0
            't.id',        # 1
            'ts.id',       # 2
            'td.title',    # 3
            'src.id',      # 4
            5, 6, 7, 8, 9, 10,
            11,
            'td.id AS delta_id',    # 12
        ],
        from       => 'src',
        inner_join => 'task_deltas td',
        on         => 'td.change_id = src.id',
        inner_join => 'topics t',
        on         => 't.id = td.task_id',
        left_join  => 'topics ts',
        on         => 'ts.id = td.task_status_id',

        # topics
        union_all_select => [
            qv('topic'),            # 0
            't.kind',               # 1
            'src.id',               # 2
            't.id',                 # 3
            4, 5, 6, 7, 8, 9, 10, 11,
            't.delta_id',           # 12
        ],
        from       => 'src',
        inner_join => 'topics t',
        on         => 't.first_change_id = src.id',

        # Order everything correctly
        order_by => 'delta_id',
    );
}

my %args = (
    change => [
        qw/
          _
          id
          parent_id
          identity_id
          mtime
          mtimetz
          author
          email
          lang
          message
          ucount
          itime
          /
    ],
    change_delta => [
        qw/
          _
          action_topic_id_1
          action_topic_id_2
          action_format
          change_id
          new
          /
    ],
    entity => [
        qw/
          _
          id
          name
          contact_id
          default_contact_method_id
          change_id
          /
    ],
    entity_delta => [
        qw/
          _
          entity_id
          name
          contact_id
          default_contact_method_id
          change_id
          /
    ],
    entity_contact_method => [
        qw/
          _
          id
          method
          mvalue
          entity_id
          change_id
          /
    ],
    entity_contact_method_delta => [
        qw/
          _
          entity_contact_method_id
          method
          mvalue
          /,
        undef,
        qw/
          change_id
          /
    ],
    hub => [
        qw/
          _
          id
          name
          change_id
          /
    ],
    hub_delta => [
        qw/
          _
          hub_id
          name
          change_id
          /
    ],
    hub_repo => [
        qw/
          _
          id
          hub_id
          location
          change_id
          /
    ],
    hub_repo_delta => [
        qw/
          _
          hub_repo_id/, undef,
        qw/
          location
          change_id
          /
    ],
    identity => [
        qw/
          _
          id
          shortname
          change_id
          /
    ],
    identity_delta => [
        qw/
          _
          identity_id
          shortname
          change_id
          /
    ],
    issue => [
        qw/
          _
          id
          issue_status_id
          title
          change_id
          src_id
          /
    ],
    issue_delta => [
        qw/
          _
          issue_id
          issue_status_id
          title
          change_id
          /,
    ],
    issue_status => [
        qw/
          _
          id
          project_id
          status
          def
          rank
          change_id
          /
    ],
    issue_status_delta => [
        qw/
          issue_status_id/, undef,
        qw/
          _
          status
          def
          rank
          change_id
          /
    ],
    project => [
        qw/
          _
          id
          parent_id
          name
          title
          /, undef, qw/
          hub_id
          change_id
          local
          /
    ],
    project_delta => [
        qw/
          _
          project_id
          parent_id
          name
          title
          project_status_id
          hub_id
          change_id
          /
    ],
    project_status => [
        qw/
          _
          id
          project_id
          status
          rank
          change_id
          /
    ],
    project_status_delta => [
        qw/
          _
          project_status_id/, undef,
        qw/
          status
          rank
          change_id
          /
    ],
    task => [
        qw/
          _
          id
          task_status_id
          title
          change_id
          /
    ],
    task_delta => [
        qw/
          _
          task_id
          task_status_id
          title
          change_id
          /
    ],
    task_status => [
        qw/
          _
          id
          project_id
          status
          def
          rank
          change_id
          /
    ],
    task_status_delta => [
        qw/
          _
          task_status_id/, undef,
        qw/
          status
          def
          rank
          change_id
          /
    ],
    topic => [
        qw/
          _
          kind
          change_id
          id
          /
    ],
);

sub Bif::DB::st::changeset_v1 {
    my $self = shift;

    my $i = 0;
    my $dcount;
    my @changeet;
    my $src;

    while ( my $row = $self->arrayref ) {
        my $kind = $row->[0];
        if ( $i == 0 and $kind ne 'change' ) {
            use Data::Dumper;
            warn "first row not a 'change' but $kind (for delta_id $row->[11]) "
              . Dumper($row);
        }

        $src = $args{$kind} || ( warn "unhandled kind: $kind" && next );

        # skip column 1 (kind) and column 2 (delta_id)
        my $delta =
          { map { defined $src->[$_] ? ( $src->[$_] => $row->[$_] ) : () }
              0 .. $#$src };

        if ( $i == 0 ) {
            $dcount = delete $delta->{ucount}
              || ( warn 'missing ucount'
                && next );
        }

        push( @changeet, $delta );

        $i++;
        last if $i == $dcount;
    }

    return if 0 == $i;
    warn "delta dcount mismatch: got: $i want:$dcount" unless $i == $dcount;

    return \@changeet;
}

my $changeset_functions = {
    change                      => 'changes',
    change_delta                => 'change_deltas',
    entity_contact_method_delta => 'entity_contact_method_deltas',
    entity_contact_method       => 'func_new_entity_contact_method',
    entity_delta                => 'entity_deltas',
    entity                      => 'func_new_entity',
    hub_delta                   => 'hub_deltas',
    hub                         => 'func_new_hub',
    hub_repo_delta              => 'hub_repo_deltas',
    hub_repo                    => 'func_new_hub_repo',
    identity_delta              => 'identity_deltas',
    identity                    => 'func_new_identity',
    issue_delta                 => 'issue_deltas',
    issue                       => 'func_new_issue',
    issue_status_delta          => 'issue_status_deltas',
    issue_status                => 'func_new_issue_status',
    project_delta               => 'project_deltas',
    project                     => 'func_new_project',
    project_status_delta        => 'project_status_deltas',
    project_status              => 'func_new_project_status',
    task_delta                  => 'task_deltas',
    task                        => 'func_new_task',
    task_status_delta           => 'task_status_deltas',
    task_status                 => 'func_new_task_status',
    topic                       => 'func_new_topic',
};

sub Bif::DB::db::insert_changeset_v1 {
    my $self      = shift;
    my $changeset = shift;
    my $functions = shift || $changeset_functions;

    my $i  = 0;
    my $id = $changeset->[0]->{id};
    Carp::croak 'missing change ID' unless $id;

    foreach my $delta (@$changeset) {
        my $type = delete $delta->{_};
        Carp::croak 'missing delta type' unless defined $type;

        my $func = $changeset_functions->{$type};
        Carp::croak 'unknown delta type: ' . $type unless defined $func;

        if ( 0 == $i ) {

            # For entities in particular we may already have this
            # changeset so ignore it.
            #            my $id = $self->xval(
            #                select => 'c.id',
            #                from   => 'changes c',
            #                where  => { 'c.uuid' => $uuid },
            #            );
            #
            #            if ($id) {
            #                $self->changes_dup( $self->changes_dup + 1 );
            #                last;
            #            }
        }
        else {
            #            $delta->{change_id} = $id;
        }

        # This should be a savepoint?
        $self->xdo(
            insert_into => $func,
            values      => $delta,
        );

        $i++;
    }

    return $i;
}

1;

=head1 NAME

=for bif-doc #perl

Bif::DB::Plugin::ChangeIDv1 - read-write helper methods for a bif
database

=head1 VERSION

0.1.5_3 (2015-06-30)

=head1 SYNOPSIS

    use strict;
    use warnings;
    use Bif::DB;
    use Bif::DB::Plugin::ChangeIDv1;

    my $db = Bif::DB->connect(...);

    # Now use dbh/st methods from ChangeIDv1

=head1 DESCRIPTION

B<Bif::DB::Plugin::ChangeIDv1> adds some changeet methods to
L<Bif::DB>.

=head1 DBH METHODS

=over

=item xprepare_changeset_v1() -> $sth

=back

=head1 ST METHODS

=over

=item changeset_v1() -> ArrayRef

=back

=head1 SEE ALSO

L<Bif::DB>

=head1 AUTHOR

Mark Lawrence E<lt>nomad@null.netE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright 2013-2015 Mark Lawrence <nomad@null.net>

This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version.

