# Copyright (C) 2020-2024 Soleta Networks <info@soleta.eu>
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the
# Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.

from ogcp import app
from flask import session, flash
from flask_babel import _

import requests
import json

class ServerError(Exception):
    pass

def flash_once(message, category='message'):
    if '_flashes' not in session:
        session['_flashes'] = []

    if (category, message) not in session['_flashes']:
        flash(message, category)

class OGServer:
    def __init__(self, name, ip, port, api_token):
        self.name = name
        self.ip = ip
        self.port = port
        self.api_token = api_token
        self._prepare_requests()

    def _prepare_requests(self):
        self.URL = f'http://{self.ip}:{self.port}'
        self.HEADERS = {'Authorization' : self.api_token}

    def _log_http_status_code(self, res):
        if res.status_code == 400:
            err_msg = _('Invalid payload')
        elif res.status_code == 404:
            err_msg = _('Object not found')
        elif res.status_code == 405:
            err_msg = _('Method not allowed')
        elif res.status_code == 409:
            err_msg = _('Object already exists')
        elif res.status_code == 423:
            err_msg = _('Object in use')
        elif res.status_code == 501:
            err_msg = _('Cannot connect to database')
        elif res.status_code == 507:
            err_msg = _('Disk full')
        else:
            err_msg = _(f'Received status code {res.status_code}')

        flash_once(err_msg, category='error')

    def _request(self, method, path, payload, expected_status):
        try:
            res = requests.request(
                method,
                f'{self.URL}{path}',
                headers=self.HEADERS,
                json=payload,
            )
            if res.status_code not in expected_status:
                self._log_http_status_code(res)
                raise ServerError
            return res

        except requests.exceptions.ConnectionError:
            flash_once(_('Cannot connect to ogserver'), category='error')
        except requests.exceptions.Timeout:
            flash_once(_('Request to ogserver timed out'), category='error')
        except requests.exceptions.TooManyRedirects:
            flash_once(_('Too many redirects occurred while contacting ogserver'), category='error')
        except requests.exceptions.RequestException as e:
            flash_once(_('An error occurred while contacting ogserver: %(error)s', error=str(e)), category='error')
        raise ServerError

    def get(self, path, payload=None):
        return self._request('GET', path, payload, expected_status={200})

    def post(self, path, payload):
        return self._request('POST', path, payload, expected_status={200, 202})

    def delete(self, path, payload):
        return self._request('DELETE', path, payload, expected_status={200})

    @property
    def id(self):
        ip = self.ip.replace('.', '-')
        return f'server_{ip}_{self.port}'


servers = []
if {'IP', 'PORT', 'API_TOKEN'} <= app.config.keys():
    # Config file backward compatibility
    servers.append(OGServer(app.config['IP'],
                            app.config['IP'],
                            app.config['PORT'],
                            app.config['API_TOKEN']))
if app.config.get('SERVERS'):
    for server in app.config['SERVERS']:
        ogserver = OGServer(server['NAME'],
                            server['IP'],
                            server['PORT'],
                            server['API_TOKEN'])
        servers.append(ogserver)
