package easydo.technology.core.processors;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

import easydo.technology.core.EasydoCoreDevice.EasydoDevice;
import easydo.technology.core.EasydoCoreDevice.EasydoDeviceHealth;
import easydo.technology.core.EasydoCoreDevice.EasydoSensor;
import easydo.technology.core.EasydoCoreDevice.EasydoSensorRecord;
import zmo.technology.lite.core.ZeroProcessor;
import zmo.technology.lite.core.ZeroRequestGuider;

public class EasydoDeviceProcessor extends ZeroProcessor {

    protected EasydoDeviceProcessor(ZeroRequestGuider guider) {
        super(guider);
    }

    public static EasydoDeviceProcessor build(ZeroRequestGuider guider) {
        return new EasydoDeviceProcessor(guider);
    }

    /// ---- deivce

    private PreparedStatement addDeivcePrepared() throws SQLException {
        final String addDeivce = "INSERT INTO easydo_device(ID, features, device_identifier, device_type) VALUES (? ,? ,? ,?)";
        if (!existsPreparedStatement(addDeivce))
            registeyPreparedStatement(addDeivce);
        return preparedStatement(addDeivce);
    }

    public void addDeivce(EasydoDevice device) throws SQLException {
        device.reset();
        addDeivcePrepared().setString(1, device.uuIDString());
        addDeivcePrepared().setString(2, device.featuresJSONString());
        addDeivcePrepared().setString(3, device.getDeviceIdentifier());
        addDeivcePrepared().setString(4, device.getDeviceType());
        addDeivcePrepared().execute();
    }

    private PreparedStatement upDeivcePrepared() throws SQLException {
        final String upDeivce = "UPDATE easydo_device SET device_identifier = ?, device_type = ?, features = ? WHERE ID = ?";
        if (!existsPreparedStatement(upDeivce))
            registeyPreparedStatement(upDeivce);
        return preparedStatement(upDeivce);
    }

    public void upDeivce(EasydoDevice device) throws SQLException {
        upDeivcePrepared().setString(1, device.getDeviceIdentifier());
        upDeivcePrepared().setString(2, device.getDeviceType());
        upDeivcePrepared().setString(3, device.featuresJSONString());
        upDeivcePrepared().setString(4, device.uuIDString());
        upDeivcePrepared().execute();
    }

    private PreparedStatement rmDeivcePrepared() throws SQLException {
        final String rmDeivce = "DELETE FROM easydo_device WHERE ID = ?";
        if (!existsPreparedStatement(rmDeivce))
            registeyPreparedStatement(rmDeivce);
        return preparedStatement(rmDeivce);
    }

    public void rmDeivce(EasydoDevice device) throws SQLException {
        rmDeivcePrepared().setString(1, device.uuIDString());
        rmDeivcePrepared().execute();
    }

    private PreparedStatement fetchDeviceByIDPrepared() throws SQLException {
        final String fetchDeviceByID = "SELECT * FROM easydo_device WHERE ID = ? LIMIT 1";
        if (!existsPreparedStatement(fetchDeviceByID))
            registeyPreparedStatement(fetchDeviceByID);
        return preparedStatement(fetchDeviceByID);
    }

    public EasydoDevice fetchDeviceByID(String deviceID) throws SQLException {
        fetchDeviceByIDPrepared().setString(1, deviceID);
        ResultSet set = fetchDeviceByIDPrepared().executeQuery();
        if (!set.next())
            throw new SQLException("device '" + deviceID + "' not found");
        return new EasydoDevice(set);
    }

    /// ---- deivce health

    private PreparedStatement addDeivceHealthPrepared() throws SQLException {
        final String addDeivceHealth = "INSERT INTO easydo_device_health(ID, features, device_id) VALUES (? ,? ,?)";
        if (!existsPreparedStatement(addDeivceHealth))
            registeyPreparedStatement(addDeivceHealth);
        return preparedStatement(addDeivceHealth);
    }

    public void addDeivceHealth(EasydoDeviceHealth deviceHealth) throws SQLException {
        deviceHealth.reset();
        addDeivceHealthPrepared().setString(1, deviceHealth.uuIDString());
        addDeivceHealthPrepared().setString(2, deviceHealth.featuresJSONString());
        addDeivceHealthPrepared().setString(3, deviceHealth.getDevice().uuIDString());
        addDeivceHealthPrepared().execute();
    }

    private PreparedStatement upDeivceHealthPrepared() throws SQLException {
        final String upDeivceHealth = "UPDATE easydo_device_health SET features = ? WHERE device_id = ? AND ID <> ''";
        if (!existsPreparedStatement(upDeivceHealth))
            registeyPreparedStatement(upDeivceHealth);
        return preparedStatement(upDeivceHealth);
    }

    public void upDeivceHealth(EasydoDeviceHealth deviceHealth) throws SQLException {
        upDeivceHealthPrepared().setString(1, deviceHealth.featuresJSONString());
        upDeivceHealthPrepared().setString(2, deviceHealth.getDevice().uuIDString());
        upDeivceHealthPrepared().execute();
    }

    private PreparedStatement fetchDeivceHealthCountPrepared() throws SQLException {
        final String fetchDeivceHealthCount = "SELECT count(1) FROM easydo_device_health WHERE device_id = ?";
        if (!existsPreparedStatement(fetchDeivceHealthCount))
            registeyPreparedStatement(fetchDeivceHealthCount);
        return preparedStatement(fetchDeivceHealthCount);
    }

    public long fetchDeivceHealthCount(String deviceID) throws SQLException {
        fetchDeivceHealthCountPrepared().setString(1, deviceID);
        ResultSet set = fetchDeivceHealthCountPrepared().executeQuery();
        if (!set.next())
            throw new SQLException("query `fetchDeivceHealthCount` faild");
        return set.getLong(1);
    }

    private PreparedStatement fetchDeivceHealthPrepared() throws SQLException {
        final String fetchDeivceHealth = "SELECT * FROM easydo_device_health WHERE device_id = ? LIMIT 1";
        if (!existsPreparedStatement(fetchDeivceHealth))
            registeyPreparedStatement(fetchDeivceHealth);
        return preparedStatement(fetchDeivceHealth);
    }

    public EasydoDeviceHealth fetchDeivceHealth(String deviceID) throws SQLException {
        fetchDeivceHealthPrepared().setString(1, deviceID);
        ResultSet set = fetchDeivceHealthPrepared().executeQuery();
        if (!set.next())
            throw new SQLException("device `" + deviceID + "` health not found");
        return new EasydoDeviceHealth(set);
    }

    public EasydoDeviceHealth fetchDeivceHealth(EasydoDevice device) throws SQLException {
        device.setHealths(new ArrayList<>());
        device.getHealths().add(fetchDeivceHealth(device.uuIDString()));
        return device.getHealths().get(0);
    }

    /// ---- sensor

    private PreparedStatement addSensorPrepared() throws SQLException {
        final String addSensor = "INSERT INTO easydo_sensor(ID, features, device_id, sensor_identifier, sensor_type) VALUES (? ,? ,? ,? ,?)";
        if (!existsPreparedStatement(addSensor))
            registeyPreparedStatement(addSensor);
        return preparedStatement(addSensor);
    }

    public void addSensor(EasydoSensor sensor) throws SQLException {
        sensor.reset();
        addSensorPrepared().setString(1, sensor.uuIDString());
        addSensorPrepared().setString(2, sensor.featuresJSONString());
        addSensorPrepared().setString(3, sensor.getDevice().uuIDString());
        addSensorPrepared().setString(4, sensor.getSensorIdentifier());
        addSensorPrepared().setString(5, sensor.getSensorType());
        addSensorPrepared().execute();
    }

    private PreparedStatement upSensorPrepared() throws SQLException {
        final String upSensor = "UPDATE easydo_sensor SET sensor_identifier = ?, sensor_type = ?, features = ? WHERE ID = ?";
        if (!existsPreparedStatement(upSensor))
            registeyPreparedStatement(upSensor);
        return preparedStatement(upSensor);
    }

    public void upSensor(EasydoSensor sensor) throws SQLException {
        upSensorPrepared().setString(1, sensor.getSensorIdentifier());
        upSensorPrepared().setString(2, sensor.getSensorType());
        upSensorPrepared().setString(3, sensor.featuresJSONString());
        upSensorPrepared().setString(4, sensor.uuIDString());
        upSensorPrepared().execute();
    }

    private PreparedStatement rmSensorPrepared() throws SQLException {
        final String rmSensor = "DELETE FROM easydo_sensor WHERE ID = ?";
        if (!existsPreparedStatement(rmSensor))
            registeyPreparedStatement(rmSensor);
        return preparedStatement(rmSensor);
    }

    public void rmSensor(EasydoSensor sensor) throws SQLException {
        rmSensorPrepared().setString(1, sensor.uuIDString());
        rmSensorPrepared().execute();
    }

    private PreparedStatement fetchSensorByIDPrepared() throws SQLException {
        final String fetchSensorByID = "SELECT * FROM easydo_sensor WHERE ID = ? LIMIT 1";
        if (!existsPreparedStatement(fetchSensorByID))
            registeyPreparedStatement(fetchSensorByID);
        return preparedStatement(fetchSensorByID);
    }

    public EasydoSensor fetchSensorByID(String sensorID) throws SQLException {
        fetchSensorByIDPrepared().setString(1, sensorID);
        ResultSet set = fetchSensorByIDPrepared().executeQuery();
        if (!set.next())
            throw new SQLException("sensor '" + sensorID + "' not found");
        return new EasydoSensor(set);
    }

    private PreparedStatement fetchSensorByDeviceIDPrepared() throws SQLException {
        final String fetchSensorByDeviceID = "SELECT * FROM easydo_sensor WHERE device_id = ? LIMIT 1";
        if (!existsPreparedStatement(fetchSensorByDeviceID))
            registeyPreparedStatement(fetchSensorByDeviceID);
        return preparedStatement(fetchSensorByDeviceID);
    }

    public List<EasydoSensor> fetchSensorByDeviceID(EasydoDevice device) throws SQLException {
        fetchSensorByDeviceIDPrepared().setString(1, device.uuIDString());
        ResultSet set = fetchSensorByDeviceIDPrepared().executeQuery();
        device.setSensors(new ArrayList<>());
        while (set.next())
            device.getSensors().add(new EasydoSensor(set));
        return device.getSensors();
    }

    /// ---- sensor record

    private PreparedStatement addSensorRecordPrepared() throws SQLException {
        final String addSensorRecord = "INSERT INTO easydo_sensor_record(ID, features, sensor_id, report_time) VALUES (? ,? ,? ,?)";
        if (!existsPreparedStatement(addSensorRecord))
            registeyPreparedStatement(addSensorRecord);
        return preparedStatement(addSensorRecord);
    }

    public void addSensorRecord(EasydoSensorRecord sensorRecord) throws SQLException {
        sensorRecord.reset();
        addSensorRecordPrepared().setString(1, sensorRecord.uuIDString());
        addSensorRecordPrepared().setString(2, sensorRecord.featuresJSONString());
        addSensorRecordPrepared().setString(3, sensorRecord.getSensor().uuIDString());
        addSensorRecordPrepared().setTimestamp(4, new Timestamp(sensorRecord.getReportTime().getTime()));
        addSensorRecordPrepared().execute();
    }

    /// ---- project devices

    private PreparedStatement addProjectDevicesPrepared() throws SQLException {
        final String addProjectDevices = "INSERT INTO easydo_project_devices(project_id, device_id) VALUES (? ,?)";
        if (!existsPreparedStatement(addProjectDevices))
            registeyPreparedStatement(addProjectDevices);
        return preparedStatement(addProjectDevices);
    }

    public void addProjectDevices(String projectID, String deviceID) throws SQLException {
        addProjectDevicesPrepared().setString(1, projectID);
        addProjectDevicesPrepared().setString(2, deviceID);
        addProjectDevicesPrepared().execute();
    }

    private PreparedStatement rmProjectDevicesPrepared() throws SQLException {
        final String rmProjectDevices = "DELETE FROM easydo_project_devices WHERE project_id = ? AND device_id = ?";
        if (!existsPreparedStatement(rmProjectDevices))
            registeyPreparedStatement(rmProjectDevices);
        return preparedStatement(rmProjectDevices);
    }

    public void rmProjectDevices(String projectID, String deviceID) throws SQLException {
        rmProjectDevicesPrepared().setString(1, projectID);
        rmProjectDevicesPrepared().setString(2, deviceID);
        rmProjectDevicesPrepared().execute();
    }
}
