package Newslash::Model::Polls;
use Newslash::Model::Base -base;
use Newslash::Model::Polls::Voters;
use Data::Dumper;


sub count {
    my $self = shift;
    return $self->generic_count(table => "pollquestions",
                                target => "qid",
                                timestamp => "date",
                                @_);
}

sub answers {
    return shift->new_instance_of("::Polls::Answers");
};

sub voters {
    return shift->new_instance_of("::Polls::Voters");
};

sub select {
    my $self = shift;
    my $params = {@_};

    my $unique_keys = { question_id => "pollquestions.qid",
                        qid => "pollquestions.qid",
                        id => "pollquestions.qid",
                        discussion => "pollquestions.discussion",
                        discussion_id => "pollquestions.discussion",
                      };
    my $keys = { create_time => "pollquestions.date",
                 voters => "pollquestions.voters",
               };
    my $datetime_keys = { create_time => "pollquestions.date",
                        };
    my $timestamp = "pollquestions.date";

    my ($where_clause, $where_values, $unique) = $self->build_where_clause(unique_keys => $unique_keys,
                                                                           keys => $keys,
                                                                           datetime_keys => $datetime_keys,
                                                                           timestamp => $timestamp,
                                                                           params => $params);
    my ($limit_clause, $limit_values) = $self->build_limit_clause(params => $params);
    my ($orderby_clause, $orderby_values) = $self->build_order_by_clause(keys => $keys,
                                                                         params => $params);

    $limit_clause = "LIMIT 20" if !$limit_clause;

    # show future story?
    my @where_clauses;
    if (!$params->{show_future}) {
        push @where_clauses, "pollquestions.date <= NOW()";
    }
    if (@where_clauses) {
        if ($where_clause) {
            $where_clause = $where_clause . " AND ";
        }
        else {
            $where_clause = "WHERE ";
        }
        $where_clause = $where_clause . join(" AND ", @where_clauses);
    }

    my @attrs;
    push @attrs, @$where_values, @$limit_values, @$orderby_values;

    my $sql = <<"EOSQL";
SELECT pollquestions.*,
       users.nickname AS author,
       topics.*,
       discussions.type AS discussion_type, discussions.commentcount AS comment_count
  FROM pollquestions
    LEFT JOIN users ON pollquestions.uid = users.uid
    LEFT JOIN discussions ON pollquestions.discussion = discussions.id
    LEFT JOIN topics ON pollquestions.topic = topics.tid
  $where_clause
  $orderby_clause
  $limit_clause
EOSQL

    my $dbh = $self->connect_db;
    my $sth = $dbh->prepare($sql);
    $sth->execute(@attrs);
    my $polls = $sth->fetchall_arrayref(+{});
    if (!$polls) {
        $self->disconnect_db();
        return;
    }
    $self->disconnect_db();

    for my $poll (@$polls) {
        $self->_generalize($poll);
        my $answers = $self->generic_select(table => "pollanswers",
                                            keys => [qw(qid aid)],
                                            params => {
                                                       qid => $poll->{qid},
                                                       order_by => { aid => 'ASC' },
                                                      });
        next if !$answers;
        $poll->{answers} = $answers;
        $self->_calc_percentage($answers);
    }

    if ($unique) {
        return $polls->[0];
    }

    return $polls;
}

sub _calc_percentage {
    my ($self, $answers) = @_;
    my $total = 0;
    for my $answer (@$answers) {
        $total += $answer->{votes};
    }
    for my $answer (@$answers) {
        if ($total > 0) {
            $answer->{percentage} = int(100 * $answer->{votes} / $total);
        }
    }
}

sub create {
    my ($self, $user, $params) = @_;
    return if $self->check_readonly;
    return if !$user || !$params;

    my $sql = <<"EOSQL";
INSERT INTO pollquestions
    (question, voters, topic, date,  uid, primaryskid)
  VALUES
    (?,        0,      ?,     NOW(), ?,   ?)
EOSQL

    my $dbh = $self->start_transaction;
    my $rs = $dbh->do($sql,
                      undef,
                      $params->{question},
                      $params->{tid},
                      $user->{uid},
                      1);
    if (!$rs) {
        $self->set_error("insert question failed", $dbh->{mysql_errorno});
        $self->rollback;
        return;
    }
    my $qid = $dbh->last_insert_id(undef, undef, undef, undef);

    $sql = <<"EOSQL";
INSERT INTO pollanswers
    (qid, aid, answer, votes)
  VALUES
    (?,   ?,   ?,      0)
EOSQL

    my $aid = 0;
    for my $answer (@{$params->{answers}}) {
        my $rs = $dbh->do($sql, undef, $qid, $aid, $answer);
        if (!$rs) {
            $self->set_error("insert answer failed", $dbh->{mysql_errorno});
            $self->rollback;
            return;
        }
        $aid++;
    }

    $self->commit;
    return $qid;
}

sub vote {
    my ($self, $user, $qid, $aid) = @_;
    my $uid = $user ? $user->{uid} : 0;

    if (!$uid) {
        $self->set_error("invalid user");
        return;
    }
    my $poll = $self->select(question_id => $qid || 0);
    if (!$poll) {
        $self->set_error("poll not found");
        return;
    }

    my $is_valid_answer = 0;
    if ($aid) {
        for my $answer (@{$poll->{answers}}) {
            if ($aid == $answer->{aid}) {
                $is_valid_answer = 1;
                last;
            }
        }
    }
    if (!$is_valid_answer) {
        $self->set_error("invalid answer");
        return;
    }

    # already voted?
    if ($user->{is_login}) {
        my $voters = $self->voters;
        my $votes = $voters->select(question_id => $qid, user_id => $uid);
        if (!$votes) {
            $self->set_error("select voters failed", $voters->last_errorno);
            return;
        }
        if (@$votes > 0) {
            $self->set_error("already voted");
            return;
        }
    }

    # update tables
    my $sql = <<"EOSQL";
UPDATE pollanswers SET votes = votes + 1 WHERE qid = ? AND aid = ?
EOSQL

    my $dbh = $self->start_transaction;
    my $rs = $dbh->do($sql, undef, $qid, $aid);
    if (!$rs) {
        $self->set_error("update pollanswers failed", $dbh->{mysql_errorno});
        $self->rollback;
        return;
    }

    $sql = <<"EOSQL";
UPDATE pollquestions SET voters = voters + 1 WHERE qid = ?
EOSQL

    $rs = $dbh->do($sql, undef, $qid);
    if (!$rs) {
        $self->set_error("update pollquestions failed", $dbh->{mysql_errorno});
        $self->rollback;
        return;
    }

    $sql = <<"EOSQL";
INSERT INTO pollvoters
    (qid, id, time,  uid)
  VALUES
    (?,    ?, NOW(), ?)
EOSQL

    my $net_id = $user->{ipid} || "";

    $rs = $dbh->do($sql, undef, $qid, $net_id, $uid);
    if (!$rs) {
        $self->set_error("insert into pollvoters failed", $dbh->{mysql_errorno});
        $self->rollback;
        return;
    }

    $self->commit;
    return 1;
}

sub hard_delete {
    my ($self, $qid) = @_;
    return if !$qid;

    my $sql = "DELETE FROM pollanswers WHERE qid = ?";

    my $dbh = $self->start_transaction;
    my $rs = $dbh->do($sql, undef, $qid);
    if (!$rs) {
        $self->set_error("hard-delete answers failed", $dbh->{mysql_errorno});
        $self->rollback;
        return;
    }

    $sql = "DELETE FROM pollquestions WHERE qid = ?";
    $rs = $dbh->do($sql, undef, $qid);
    if (!$rs) {
        $self->set_error("hard-delete questions failed", $dbh->{mysql_errorno});
        $self->rollback;
        return;
    }

    $sql = "DELETE FROM pollvoters WHERE qid = ?";
    $rs = $dbh->do($sql, undef, $qid);
    if (!$rs) {
        $self->set_error("hard-delete voters failed", $dbh->{mysql_errorno});
        $self->rollback;
        return;
    }

    $self->commit;
    return 1;
}

sub _generalize {
    my ($self, $poll) = @_;

    # NTO-nized
    $poll->{id} = $poll->{qid};
    $poll->{create_time} = $poll->{date};

    my $primary_topic = {};
    for my $item (qw(tid keyword textname series image width height submittable searchable storypickable usesprite)) {
        $primary_topic->{$item} = $poll->{$item};
    }
    $poll->{primary_topic} = $primary_topic;
    $poll->{topics} = [$primary_topic,];

    if ($poll->{topic}) {
        $poll->{primary_topic} = $poll->{topic};
    }
    $poll->{content_type} = "poll";
    $poll->{title} = $poll->{question};
    $poll->{discussion_id} = $poll->{discussion};
    $poll->{public} = 'yes';

    return $poll;
}


1;
