#!/usr/bin/env python3

from pathlib import Path
import mimetypes
import os
import sys
from urllib.parse import urlsplit

sys.dont_write_bytecode = True

APP_ROOT = Path(__file__).resolve().parent

if str(APP_ROOT) not in sys.path:
    sys.path.insert(0, str(APP_ROOT))

if str(APP_ROOT / 'api') not in sys.path:
    sys.path.insert(0, str(APP_ROOT / 'api'))

from api.app import application as api_application

PUBLIC_HOSTS = {
    'amanaliterature.ng',
    'www.amanaliterature.ng',
}

MOBILE_HOSTS = {
    'm.amanaliterature.ng',
}

STATIC_ROOTS = (
    'public_web',
    'member_web',
    'admin_web',
    'static',
    'config',
)

ROOT_PUBLIC_ASSETS = {
    'app.js',
    'styles.css',
}

ROOT_MEMBER_ASSETS = {
    'app.js',
    'styles.css',
    'app_config.js',
}


def _host(environ):
    raw = str(
        environ.get('HTTP_HOST')
        or environ.get('SERVER_NAME')
        or ''
    ).split(':', 1)[0].lower()

    return raw.strip('.')


def _request_path(environ):
    raw = environ.get('PATH_INFO')

    if raw in (None, ''):
        raw = (
            environ.get('REQUEST_URI')
            or environ.get('RAW_URI')
            or '/'
        )

    path = urlsplit(str(raw)).path or '/'

    if not path.startswith('/'):
        path = '/' + path

    return path


def _inside(child, parent):
    try:
        return (
            os.path.commonpath(
                [str(child.resolve()), str(parent.resolve())]
            )
            == str(parent.resolve())
        )
    except ValueError:
        return False


def _candidate_from(path):
    candidate = (APP_ROOT / path.lstrip('/')).resolve()

    for root_name in STATIC_ROOTS:
        allowed_root = APP_ROOT / root_name

        if _inside(candidate, allowed_root) and candidate.is_file():
            return candidate

    return None


def _static_alias(path_info, host):
    path = urlsplit(str(path_info or '/')).path or '/'

    mobile_host = host in MOBILE_HOSTS

    if path in {'/', '', '/index.html', '/index.htm'}:
        if mobile_host:
            return 'member_web/index.html'
        return 'public_web/index.html'

    if path in {
        '/admin',
        '/admin/',
        '/admin/index.html',
        '/admin/index.htm',
    }:
        return 'admin_web/index.html'

    if path.startswith('/admin/'):
        return 'admin_web/' + path[len('/admin/'):]

    if path in {
        '/member',
        '/member/',
        '/member/index.html',
        '/member/index.htm',
        '/mobile',
        '/mobile/',
    }:
        return 'member_web/index.html'

    if path.startswith('/member/'):
        return 'member_web/' + path[len('/member/'):]

    if path.startswith('/mobile/'):
        return 'member_web/' + path[len('/mobile/'):]

    for root_name in STATIC_ROOTS:
        prefix = '/' + root_name + '/'

        if path == '/' + root_name:
            return root_name + '/index.html'

        if path.startswith(prefix):
            return path.lstrip('/')

    leaf = path.lstrip('/')

    if '/' not in leaf:
        if mobile_host and leaf in ROOT_MEMBER_ASSETS:
            return 'member_web/' + leaf

        if not mobile_host and leaf in ROOT_PUBLIC_ASSETS:
            return 'public_web/' + leaf

    return path.lstrip('/')


def _static_candidate(path_info, host):
    alias = _static_alias(path_info, host)

    if not alias:
        return None

    return _candidate_from(alias)


def _serve_static(path, start_response):
    content_type = (
        mimetypes.guess_type(str(path))[0]
        or 'application/octet-stream'
    )

    suffix = ''

    if (
        content_type.startswith('text/')
        or content_type in {
            'application/javascript',
            'application/json',
        }
    ):
        suffix = '; charset=utf-8'

    body = path.read_bytes()

    start_response(
        '200 OK',
        [
            ('Content-Type', content_type + suffix),
            ('Content-Length', str(len(body))),
        ],
    )

    return [body]


def _not_found(start_response):
    body = b'Amana Literature Ministry NG resource not found.'

    start_response(
        '404 Not Found',
        [
            ('Content-Type', 'text/plain; charset=utf-8'),
            ('Content-Length', str(len(body))),
        ],
    )

    return [body]


def application(environ, start_response):
    environ.setdefault('CIL_APP_ROOT', str(APP_ROOT))
    environ.setdefault('CIL_WORKSPACE_ROOT', str(APP_ROOT.parent))

    path_info = _request_path(environ)

    if path_info == '/api':
        environ['PATH_INFO'] = '/api/health'
        return api_application(environ, start_response)

    if path_info.startswith('/api/'):
        return api_application(environ, start_response)

    candidate = _static_candidate(
        path_info,
        _host(environ),
    )

    if candidate:
        return _serve_static(candidate, start_response)

    return _not_found(start_response)