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

our $VERSION = '0.1.5_3';

my %statements = (
    new_hub => [
        select     => [ '"new_hub" AS _delta', 't.uuid AS uuid', ],
        from       => 'hub_deltas hd',
        inner_join => 'topics t',
        on         => 't.id = hd.hub_id',
        where      => 'hd.id = ?',
    ],

    update_hub => [
        select => [
            '"update_hub" AS _delta',
            'pd.name AS name',
            'pd.title AS title',
            't.uuid AS uuid',
        ],
        from       => 'project_deltas pd',
        inner_join => 'topics t',
        on         => 't.id = pd.project_id',
        where      => 'pd.id = ?',
    ],

    new_hub_repo => [
        select => [
            '"new_hub_repo" AS _delta',
            'h.uuid AS hub_uuid',
            'hrd.location AS location',
            'hr.uuid AS uuid',
        ],
        from       => 'hub_repo_deltas hrd',
        inner_join => 'topics hr',
        on         => 'hr.id = hrd.hub_repo_id',
        left_join  => 'topics h',
        on         => 'h.id = hrd.hub_id',
        where      => 'hrd.id = ?',
    ],

    update_hub_repo => [
        select => [
            '"update_hub_repo" AS _delta',
            'h.uuid AS hub_uuid',    # should never change, remove?
            'hrd.location AS location',
            'hr.uuid AS uuid',
        ],
        from       => 'hub_repo_deltas hrd',
        inner_join => 'topics hr',
        on         => 'hr.id = hrd.hub_repo_id',
        left_join  => 'topics h',
        on         => 'h.id = hrd.hub_id',
        where      => 'hrd.id = ?',
    ],

    new_identity => [
        select => [
            '"new_identity" AS _delta',
            'ed.name AS name',
            'id.shortname AS shortname',
            'con.uuid AS contact_uuid',
            'dcm.uuid AS default_contact_method_uuid',
            'i.uuid AS uuid',
        ],
        from       => 'identity_deltas id',
        inner_join => 'entity_deltas ed',
        on         => 'ed.id = id.id',
        inner_join => 'topics i',
        on         => 'i.id = id.identity_id',
        left_join  => 'topics con',
        on         => 'con.id = ed.contact_id',
        left_join  => 'topics dcm',
        on         => 'dcm.id = ed.default_contact_method_id',
        where      => 'id.id = ?',
    ],

    update_identity => [
        select => [
            '"update_identity" AS _delta',
            't.uuid AS uuid',
            'ed.name AS name',
            'id.shortname AS shortname',
            'con.uuid AS contact_uuid',
            'dcm.uuid AS default_contact_method_uuid',
        ],
        from       => 'identity_deltas id',
        inner_join => 'entity_deltas ed',
        on         => 'ed.id = id.id',
        inner_join => 'topics t',
        on         => 't.id = id.identity_id',
        left_join  => 'topics con',
        on         => 'con.id = ed.contact_id',
        left_join  => 'topics dcm',
        on         => 'dcm.id = ed.default_contact_method_id',
        where      => 'id.id = ?',
    ],

    new_identity_contact_method => [
        select => [
            '"new_identity_contact_method" AS _delta',
            'i.uuid AS identity_uuid',
            'ecmd.method AS method',
            'ecmd.mvalue AS mvalue',
            'ecmt.uuid AS uuid',
        ],
        from       => 'entity_contact_method_deltas ecmd',
        inner_join => 'entity_contact_methods ecm',
        on         => 'ecm.id = ecmd.entity_contact_method_id',
        inner_join => 'topics ecmt',
        on         => 'ecmt.id = ecm.id',
        inner_join => 'topics i',
        on         => 'i.id = ecm.entity_id',
        where      => 'ecmd.id = ?',
    ],

    update_identity_contact_method => [
        select => [
            't.uuid AS identity_contact_method_uuid',
            'ecmd.method AS method',
            'ecmd.mvalue AS mvalue',
        ],
        from       => 'entity_contact_method_deltas ecmd',
        inner_join => 'topics t',
        on         => 't.id = ecmd.id',
        where      => 'ecmd.id = ?',
    ],

    new_issue => [
        select => [
            '"new_issue" AS _delta',
            's.uuid AS issue_status_uuid',
            'src.uuid AS src_uuid',
            'id.title AS title',
            't.uuid AS uuid',
        ],
        from       => 'issue_deltas id',
        inner_join => 'topics t',
        on         => 't.id = id.issue_id',
        inner_join => 'topics s',
        on         => 's.id = id.issue_status_id',
        left_join  => 'issues i',
        on         => 'i.id = id.issue_id',
        left_join  => 'topics src',
        on         => 'src.id = i.src_id',
        where      => 'id.id = ?',
    ],

    update_issue => [
        select => [
            '"update_issue" AS _delta',
            'ist.uuid AS issue_status_uuid',
            'id.title AS title',
            't.uuid AS uuid',
        ],
        from       => 'issue_deltas id',
        inner_join => 'topics t',
        on         => 't.id = id.issue_id',
        left_join  => 'topics ist',
        on         => 'ist.id = id.issue_status_id',
        where      => 'id.id = ?',
    ],

    new_issue_status => [
        select => [
            '"new_issue_status" AS _delta',
            'isd.def AS def',
            'p.uuid AS project_uuid',
            'isd.rank AS rank',
            'isd.status AS status',
            't.uuid AS uuid',
        ],
        from       => 'issue_status_deltas isd',
        inner_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',
        where      => 'isd.id = ?',
    ],

    update_issue_status => [
        select => [
            '"update_issue_status" AS _delta',
            'isd.def AS def',
            'isd.rank AS rank',
            'isd.status AS status',
            'ist.uuid AS uuid',
        ],
        from       => 'issue_status_deltas isd',
        inner_join => 'topics ist',
        on         => 'ist.id = ist.id',
        where      => 'isd.id = ?',
    ],

    new_project => [
        select => [
            '"new_project" AS _delta',
            'pd.name AS name',
            'pp.uuid AS parent_uuid',
            'pd.title AS title',
            'p.uuid AS uuid',
            'wd.delta AS work_delta',
        ],
        from       => 'project_deltas pd',
        inner_join => 'topics p',
        on         => 'p.id = pd.project_id',
        left_join  => 'topics pp',
        on         => 'pp.id = pd.parent_id',
        left_join  => 'work_deltas wd',
        on         => 'wd.id = pd.id',
        where      => 'pd.id = ?',
    ],

    update_project => [
        select => [
            '"update_project" AS _delta',
            'pd.name AS name',
            'pp.uuid AS parent_uuid',
            'ps.uuid AS project_status_uuid',
            'pd.title AS title',
            'p.uuid AS uuid',
            'wd.delta AS work_delta',
        ],
        from       => 'project_deltas pd',
        inner_join => 'topics p',
        on         => 'p.id = pd.project_id',
        left_join  => 'topics ps',
        on         => 'ps.id = pd.project_status_id',
        left_join  => 'topics pp',
        on         => 'pp.id = pd.parent_id',
        left_join  => 'work_deltas wd',
        on         => 'wd.id = pd.id',
        where      => 'pd.id = ?',
    ],

    new_project_status => [
        select => [
            '"new_project_status" AS _delta',
            'p.uuid AS project_uuid',
            'psd.rank AS rank',
            'psd.status AS status',
            't.uuid AS uuid',
        ],
        from       => 'project_status_deltas psd',
        inner_join => 'project_status ps',
        on         => 'ps.id = psd.project_status_id',
        inner_join => 'topics t',
        on         => 't.id = ps.id',
        inner_join => 'topics p',
        on         => 'p.id = ps.project_id',
        where      => 'psd.id = ?',
    ],

    update_project_status => [
        select => [
            '"update_project_status" AS _delta',
            'psd.rank AS rank',
            'psd.status AS status',
            'ps.uuid AS uuid',
        ],
        from       => 'project_status_deltas psd',
        inner_join => 'topics ps',
        on         => 'ps.id = psd.project_status_id',
        where      => 'psd.id = ?',
    ],

    new_task => [
        select => [
            '"new_task" AS _delta',
            'ts.uuid AS task_status_uuid',
            'td.title AS title',
            't.uuid AS uuid',
        ],
        from       => 'task_deltas td',
        inner_join => 'topics t',
        on         => 't.id = td.task_id',
        inner_join => 'topics ts',
        on         => 'ts.id = td.task_status_id',
        where      => 'td.id = ?',
    ],

    update_task => [
        select => [
            '"update_task" AS _delta',
            'ts.uuid AS task_status_uuid',
            'td.title AS title',
            't.uuid AS uuid',
        ],
        from       => 'task_deltas td',
        inner_join => 'topics t',
        on         => 't.id = td.task_id',
        left_join  => 'topics ts',
        on         => 'ts.id = td.task_status_id',
        where      => 'td.id = ?',
    ],

    new_task_status => [
        select => [
            '"new_task_status" AS _delta',
            'tsd.def AS def',
            'p.uuid AS project_uuid',
            'tsd.rank AS rank',
            'tsd.status AS status',
            't.uuid AS uuid',
        ],
        from       => 'task_status_deltas tsd',
        inner_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',
        where      => 'tsd.id = ?',
    ],

    update_task_status => [
        select => [
            '"update_task_status" AS _delta',
            'tsd.def AS def',
            'tsd.rank AS rank',
            'tsd.status AS status',
            'ts.uuid AS uuid',
        ],
        from       => 'task_status_deltas tsd',
        inner_join => 'topics ts',
        on         => 'ts.id = ts.id',
        where      => 'tsd.id = ?',
    ],

);

my %prepared;

sub Bif::DB::db::uchangeset_v1 {
    my $self      = shift;
    my $id        = shift;
    my @changeset = ();

    my $begin_change = $prepared{$self}{begin_change} ||= $self->xprepare(
        select => [
            'p.uuid AS parent_uuid',
            'c.mtime AS mtime',
            'c.mtimetz AS mtimetz',
            'c.author AS author',
            'c.author_contact AS author_contact',
            'c.author_contact_method AS author_contact_method',
            'c.author_shortname AS author_shortname',
            'c.lang AS lang',
        ],
        from      => 'changes c',
        left_join => 'changes p',
        on        => 'p.id = c.parent_id',
        where     => 'c.id = ?',
    );
    $begin_change->execute($id);
    push( @changeset, $begin_change->hashref );
    $begin_change->finish;

    my $deltas = $prepared{$self}{deltas} ||= $self->xprepare(
        select   => [ 'd.id AS id', 'd.action AS action', ],
        from     => 'deltas d',
        where    => 'd.change_id = ?',
        order_by => 'd.id',
    );
    $deltas->execute($id);

    my $sth;
    foreach my $delta ( $deltas->hashrefs ) {
        $sth = $prepared{$self}{ $delta->{action} } ||=
          $self->xprepare( @{ $statements{ $delta->{action} } } );
        $sth->execute( $delta->{id} );
        if ( my $ref = $sth->hashref ) {
            push( @changeset, $ref );
        }
        else {
            warn "missing delta $delta->{id}: $delta->{action}";
        }
        $sth->finish;
    }

    my $end_change = $prepared{$self}{end_change} ||= $self->xprepare(
        select => [
            'c.action_format AS action_format',
            't1.uuid AS action_topic_uuid_1',
            't2.uuid AS action_topic_uuid_2',
            'i.uuid AS identity_uuid',
            'c.message AS message',
            'c.uuid AS uuid',
        ],
        from       => 'changes c',
        inner_join => 'topics i',
        on         => 'i.id = c.identity_id',
        left_join  => 'topics t1',
        on         => 't1.id = c.action_topic_id_1',
        left_join  => 'topics t2',
        on         => 't2.id = c.action_topic_id_2',
        where      => 'c.id = ?',
    );

    $end_change->execute($id);
    push( @changeset, $end_change->hashref );
    $end_change->finish;

    return \@changeset;
}

my %import = (
    new_hub                     => 'func_import_hub',
    new_hub_repo                => 'func_import_hub_repo',
    new_identity_contact_method => 'func_import_identity_contact_method',
    new_identity                => 'func_import_identity',
    new_issue                   => 'func_import_issue',
    new_issue_status            => 'func_import_issue_status',
    new_project                 => 'func_import_project',
    new_project_status          => 'func_import_project_status',
    new_task                    => 'func_import_task',
    new_task_status             => 'func_import_task_status',
    update_hub                  => 'func_import_hub_delta',
    update_hub_repo             => 'func_import_hub_repo_delta',
    update_identity_contact_method =>
      'func_import_identity_contact_method_delta',
    update_identity => 'func_import_identity_delta',
    update_issue    => 'func_import_issue_delta',
    update_project  => 'func_import_project_delta',
    update_task     => 'func_import_task_delta',
);

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

    my $begin = shift @$changeset;
    my $end   = pop @$changeset;

    my @keys = sort keys %$begin;
    my @values = map { qv( $begin->{$_} ) } @keys;

    # Only insert if the uuid does not exist
    my $have_change = $prepared{$self}{have_change} ||= $self->xprepare(
        select => 'count(id)',
        from   => 'changes c',
        where  => {
            'c.uuid' => $end->{uuid},
        },
    );
    $have_change->execute( $end->{uuid} );
    my $res = $have_change->val;
    $have_change->finish;
    return 0 if $res;

    $self->xdo(
        insert_into => 'func_import_begin_change',
        values      => $begin,
    );

    foreach my $delta (@$changeset) {
        $self->xdo(
            insert_into => $import{ delete $delta->{_delta} },
            values      => $delta,
        );
    }

    $self->xdo(
        insert_into => 'func_import_end_change',
        values      => $end,
    );

    return 1;
}

1;

=head1 NAME

=for bif-doc #perl

Bif::DB::Plugin::ChangeUUIDv1 - 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::ChangeUUIDv1;

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

    # Now use dbh/st methods from ChangeUUIDv1

=head1 DESCRIPTION

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

=head1 DBH METHODS

=over

=item xprepare_uchangeset_v1() -> $sth

=back

=head1 ST METHODS

=over

=item uchangeset_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.

