#!/usr/bin/python3
"""Application and persistence management."""

# pylint: disable=no-member, import-error, no-init, too-few-public-methods
# pylint: disable=cyclic-import, no-name-in-module, invalid-name

import os

from flask import Flask
from flask.ext import restful
from flask.ext.cors import CORS
from flask.ext.sqlalchemy import SQLAlchemy

from sqlalchemy.sql import text
from sqlalchemy.dialects.postgresql import UUID

app = Flask("storage-hcp")
cors = CORS(app)
restapi = restful.Api(app)

app.config["ERSA_STORAGE_HCP_TOKEN"] = os.getenv("ERSA_STORAGE_HCP_TOKEN")
app.config["SQLALCHEMY_DATABASE_URI"] = os.getenv("ERSA_STORAGE_HCP_DATABASE")

db = SQLAlchemy(app)


def _id_column():
    """Generate a UUID column."""
    return db.Column(UUID,
                     server_default=text("uuid_generate_v4()"),
                     primary_key=True)


class Allocation(db.Model):
    """Storage Allocation"""
    id = _id_column()
    allocation = db.Column(db.Integer, unique=True, nullable=False)
    tenants = db.relationship("Tenant", backref="allocation")
    namespaces = db.relationship("Namespace", backref="allocation")

    def json(self):
        """Jsonify"""

        return {"id": self.id, "allocation": self.allocation}


class Snapshot(db.Model):
    """Storage Snapshot"""
    id = _id_column()
    ts = db.Column(db.Integer, nullable=False)
    usage = db.relationship("Usage", backref="snapshot")

    def json(self):
        """Jsonify"""
        return {"id": self.id, "ts": self.ts}


class Tenant(db.Model):
    """HCP Tenant"""
    id = _id_column()
    name = db.Column(db.String(256), unique=True, nullable=False)
    namespaces = db.relationship("Namespace", backref="tenant")
    allocation_id = db.Column(None, db.ForeignKey("allocation.id"))

    def json(self, namespaces=True):
        """Jsonify"""

        result = {"id": self.id, "name": self.name}

        if self.allocation:
            result["allocation"] = self.allocation.json()

        if namespaces:
            result["namespaces"] = [namespace.json(tenants=False)
                                    for namespace in self.namespaces]

        return result


class Namespace(db.Model):
    """HCP Namespace"""
    id = _id_column()
    name = db.Column(db.String(256), nullable=False)
    usage = db.relationship("Usage", backref="namespace")
    tenant_id = db.Column(None,
                          db.ForeignKey("tenant.id"),
                          index=True,
                          nullable=False)
    allocation_id = db.Column(None, db.ForeignKey("allocation.id"))

    def json(self, tenants=True):
        """Jsonify"""

        result = {"id": self.id, "name": self.name}

        if self.allocation:
            result["allocation"] = self.allocation.json()

        if tenants:
            result["tenant"] = self.tenant.json(namespaces=False)

        return result


class Usage(db.Model):
    """HCP Usage"""
    id = _id_column()
    start_time = db.Column(db.Integer, index=True, nullable=False)
    end_time = db.Column(db.Integer, index=True, nullable=False)
    ingested_bytes = db.Column(db.BigInteger, nullable=False)
    raw_bytes = db.Column(db.BigInteger, nullable=False)
    reads = db.Column(db.BigInteger, nullable=False)
    writes = db.Column(db.BigInteger, nullable=False)
    deletes = db.Column(db.BigInteger, nullable=False)
    objects = db.Column(db.BigInteger, nullable=False)
    bytes_in = db.Column(db.BigInteger, nullable=False)
    bytes_out = db.Column(db.BigInteger, nullable=False)
    metadata_only_objects = db.Column(db.BigInteger, nullable=False)
    metadata_only_bytes = db.Column(db.BigInteger, nullable=False)
    tiered_objects = db.Column(db.BigInteger, nullable=False)
    tiered_bytes = db.Column(db.BigInteger, nullable=False)
    snapshot_id = db.Column(None,
                            db.ForeignKey("snapshot.id"),
                            index=True,
                            nullable=False)
    namespace_id = db.Column(None,
                             db.ForeignKey("namespace.id"),
                             index=True,
                             nullable=False)

    def json(self):
        """Jsonify"""

        return {
            "start_time": self.start_time,
            "end_time": self.end_time,
            "ingested_bytes": self.ingested_bytes,
            "raw_bytes": self.raw_bytes,
            "reads": self.reads,
            "writes": self.writes,
            "deletes": self.deletes,
            "objects": self.objects,
            "bytes_in": self.bytes_in,
            "bytes_out": self.bytes_out,
            "metadata_only_objects": self.metadata_only_objects,
            "metadata_only_bytes": self.metadata_only_bytes,
            "tiered_objects": self.tiered_objects,
            "tiered_bytes": self.tiered_bytes,
            "snapshot": self.snapshot.json(),
            "namespace": {
                "id": self.namespace.id,
                "name": self.namespace.name
            }
        }


def run():
    """Let's roll."""
    db.engine.execute("create extension if not exists \"uuid-ossp\";")
    db.create_all()

    from ersa_storage_hcp import api
    restapi.add_resource(api.PingResource, "/ping")
    restapi.add_resource(api.AllocationResource, "/allocation")
    restapi.add_resource(api.StorageResource, "/storage")
    restapi.add_resource(api.SnapshotResource, "/snapshot")
    restapi.add_resource(api.UsageResource, "/usage")

    app.run(host="127.0.0.1", port=int(os.getenv("ERSA_STORAGE_HCP_PORT")))
