package eu.dnetlib.goldoa.service;

import eu.dnetlib.goldoa.domain.Invoice;
import eu.dnetlib.goldoa.domain.InvoiceFile;
import eu.dnetlib.goldoa.domain.ManagerException;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.transaction.annotation.Transactional;

import javax.sql.DataSource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.UUID;

/**
 * Created by antleb on 3/21/15.
 */
@Transactional
public class InvoiceManagerImpl implements InvoiceManager {

    // TODO move to dao if needed
    @Autowired
    private DataSource dataSource;

    private static final String INSERT_INVOICE = "insert into invoice (number, alternativeid, date, source, id) values (?, ?, ?, ?, ?)";

    private static final String UPDATE_INVOICE = "update invoice set number=?, alternativeid=?, date=?, source=? where id = ?";

    private static final String GET_INVOICE = "select id, alternativeid, number, date, source from invoice where id = ?";

    private static final String GET_INVOICE_FILE = "select invoice, mimetype, file from file where invoice = ?";

    private static final String INSERT_INVOICE_FILE = "insert into file (mimetype, file, invoice) values (?, ?, ?)";

    private static final String UPDATE_INVOICE_FILE = "update file set mimetype=?, file=? where invoice=?";

    @Override
    public Invoice saveInvoice(Invoice invoice) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

        if (invoice.getId() == null) {
            invoice.setSource("portal");
            invoice.setId("portal::" + UUID.randomUUID().toString());
        }

        if (jdbcTemplate.update(UPDATE_INVOICE, new Object[] {invoice.getNumber(), invoice.getAlternativeId(), invoice.getDate(), invoice.getSource(), invoice.getId()},
                new int[] {Types.VARCHAR, Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR, Types.VARCHAR}) == 0) {
            jdbcTemplate.update(INSERT_INVOICE, new Object[] {invoice.getNumber(), invoice.getAlternativeId(), invoice.getDate(), invoice.getSource(), invoice.getId()},
                    new int[] {Types.VARCHAR, Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR, Types.VARCHAR});
        }

        return invoice;
    }

    @Override
    public Invoice getInvoice(String invoiceId) throws ManagerException {
        try {
            return new JdbcTemplate(dataSource).queryForObject(GET_INVOICE, new String[]{invoiceId}, new int[]{Types.VARCHAR}, new RowMapper<Invoice>() {
                @Override
                public Invoice mapRow(ResultSet rs, int rowNum) throws SQLException {
                    Invoice invoice = new Invoice();

                    invoice.setId(rs.getString("id"));
                    invoice.setAlternativeId(rs.getString("alternativeid"));
                    invoice.setNumber(rs.getString("number"));
                    invoice.setDate(rs.getTimestamp("date"));
                    invoice.setSource(rs.getString("source"));

                    return invoice;
                }
            });
        } catch (EmptyResultDataAccessException e) {
            throw new ManagerException(ManagerException.ErrorCause.NOT_EXISTS);
        }
    }

    @Override
    public void uploadInvoice(final String invoiceId, final String mimetype, InputStream invoice) throws ManagerException {
        try {
            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
            final JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
            final PreparedStatementSetter pss = new PreparedStatementSetter() {
                @Override
                public void setValues(PreparedStatement ps) throws SQLException {
                    ps.setString(1, mimetype);
                    ps.setBytes(2, baos.toByteArray());
                    ps.setString(3, invoiceId);
                }
            };

            IOUtils.copy(invoice, baos);
            IOUtils.closeQuietly(baos);

            if (jdbcTemplate.update(UPDATE_INVOICE_FILE, pss) == 0) {
                jdbcTemplate.update(INSERT_INVOICE_FILE, pss);
            }
        } catch (Exception e) {
            throw new ManagerException(ManagerException.ErrorCause.UNKNOWN);
        }
    }

    @Override
    public InvoiceFile downloadInvoice(String invoiceId) throws ManagerException {
        try {
            return new JdbcTemplate(dataSource).queryForObject(GET_INVOICE_FILE, new String[]{invoiceId}, new int[]{Types.VARCHAR}, new RowMapper<InvoiceFile>() {
                @Override
                public InvoiceFile mapRow(ResultSet rs, int rowNum) throws SQLException {
                    return new InvoiceFile(rs.getString("invoice"), rs.getString("mimetype"), rs.getBytes("file"));
                }
            });
        } catch (EmptyResultDataAccessException e) {
            throw new ManagerException(ManagerException.ErrorCause.NOT_EXISTS);
        } catch (Exception e) {
            throw new ManagerException(ManagerException.ErrorCause.UNKNOWN);
        }
    }

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}