# The Food Integrity Project API client, generated from the OpenAPI document (API 2.0.0).
# Don't edit: run `npm run api:clients` in the FIP-OS repository. Guides: https://api.foodintegrityproject.org/docs
"""The Food Integrity Project API: verified products, and verification management for technical administrators."""

import json
import urllib.error
import urllib.parse
import urllib.request


def _q(value):
    return urllib.parse.quote(str(value), safe='')


class ApiError(Exception):
    """An error answer, with its problem details (code, title, detail, field)."""

    def __init__(self, status, problem):
        self.status = status
        self.problem = problem or {}
        self.code = self.problem.get('code')
        super().__init__(self.problem.get('detail') or self.problem.get('title') or f'HTTP {status}')


class FoodIntegrityProjectApi:
    def __init__(self, token=None, base_url='https://api.foodintegrityproject.org/v2', timeout=60):
        """token: an explorer or personal token, or call sign_in(). base_url: .../sandbox/v2 for the sandbox."""
        self.token = token
        self.base_url = base_url.rstrip('/')
        self.timeout = timeout

    def sign_in(self, client_id, client_secret, scope=None):
        """Gets a token for an organization's key (client credentials with its secret) and uses it from then on."""
        form = {'grant_type': 'client_credentials', 'client_id': client_id, 'client_secret': client_secret}
        if scope:
            form['scope'] = scope
        req = urllib.request.Request(self.base_url + '/oauth/token', data=urllib.parse.urlencode(form).encode(), method='POST',
                                     headers={'Content-Type': 'application/x-www-form-urlencoded'})
        try:
            with urllib.request.urlopen(req, timeout=self.timeout) as res:
                answer = json.loads(res.read())
        except urllib.error.HTTPError as e:
            body = json.loads(e.read() or b'{}')
            raise ApiError(e.code, {'code': body.get('error'), 'detail': body.get('error_description')}) from None
        self.token = answer['access_token']
        return answer

    def request(self, method, path, query=None, body=None, idempotency_key=None, if_match=None):
        url = self.base_url + path
        params = {k: ('true' if v is True else 'false' if v is False else v) for k, v in (query or {}).items() if v is not None}
        if params:
            url += '?' + urllib.parse.urlencode(params)
        headers = {'Accept': 'application/json'}
        if self.token:
            headers['Authorization'] = 'Bearer ' + self.token
        data = None
        if body is not None:
            headers['Content-Type'] = 'application/json'
            data = json.dumps(body).encode()
        if idempotency_key:
            headers['Idempotency-Key'] = idempotency_key
        if if_match:
            headers['If-Match'] = if_match
        req = urllib.request.Request(url, data=data, method=method, headers=headers)
        try:
            with urllib.request.urlopen(req, timeout=self.timeout) as res:
                text = res.read()
                return json.loads(text) if text else None
        except urllib.error.HTTPError as e:
            text = e.read()
            raise ApiError(e.code, json.loads(text) if text else {}) from None

    def all(self, list_method, **query):
        """Every item of a cursor list, page by page: for item in api.all(api.list_products, q="granola")."""
        cursor = None
        while True:
            page = list_method(**query, **({'cursor': cursor} if cursor else {}))
            yield from page['data']
            cursor = page.get('next_cursor')
            if not cursor:
                return

    # Writes

    def add_country_of_origin(self, id, body, *, idempotency_key=None, if_match=None):
        """Add a country of origin."""
        return self.request('POST', '/products/' + _q(id) + '/countries-of-origin', None, body, idempotency_key, if_match)

    def batch(self, body, *, idempotency_key=None, if_match=None):
        """Send up to 500 operations."""
        return self.request('POST', '/batch', None, body, idempotency_key, if_match)

    def cancel_verification(self, ref, *, idempotency_key=None, if_match=None):
        """Cancel a verification."""
        return self.request('POST', '/verifications/' + _q(ref) + '/cancel', None, None, idempotency_key, if_match)

    def create_brand(self, body, *, idempotency_key=None, if_match=None):
        """Add a brand to a client."""
        return self.request('POST', '/brands', None, body, idempotency_key, if_match)

    # Verification management

    def create_certificate_pdf(self, body, *, idempotency_key=None, if_match=None):
        """Make a certificate PDF."""
        return self.request('POST', '/certificates/pdf', None, body, idempotency_key, if_match)

    # Writes

    def create_client(self, body, *, idempotency_key=None, if_match=None):
        """Send a client application."""
        return self.request('POST', '/clients', None, body, idempotency_key, if_match)

    def create_facility(self, body, *, idempotency_key=None, if_match=None):
        """Register a facility."""
        return self.request('POST', '/facilities', None, body, idempotency_key, if_match)

    def create_package(self, id, body, *, idempotency_key=None, if_match=None):
        """Add a package."""
        return self.request('POST', '/products/' + _q(id) + '/packages', None, body, idempotency_key, if_match)

    def create_verification(self, id, body, *, idempotency_key=None, if_match=None):
        """Add a verification."""
        return self.request('POST', '/products/' + _q(id) + '/verifications', None, body, idempotency_key, if_match)

    # Webhooks

    def create_webhook(self, body, *, idempotency_key=None, if_match=None):
        """Add a webhook."""
        return self.request('POST', '/webhooks', None, body, idempotency_key, if_match)

    def delete_webhook(self, id, *, idempotency_key=None, if_match=None):
        """Remove a webhook."""
        return self.request('DELETE', '/webhooks/' + _q(id), None, None, idempotency_key, if_match)

    # Catalog

    def get_brand(self, number, *, idempotency_key=None, if_match=None):
        """Get a brand."""
        return self.request('GET', '/catalog/brands/' + _q(number), None, None, idempotency_key, if_match)

    # Verification management

    def get_certificate(self, number, *, idempotency_key=None, if_match=None):
        """Get a certificate."""
        return self.request('GET', '/certificates/' + _q(number), None, None, idempotency_key, if_match)

    def get_certificate_pdf(self, job, *, idempotency_key=None, if_match=None):
        """Get a certificate job."""
        return self.request('GET', '/certificates/pdf/' + _q(job), None, None, idempotency_key, if_match)

    def get_client(self, id, *, idempotency_key=None, if_match=None):
        """Get a client."""
        return self.request('GET', '/clients/' + _q(id), None, None, idempotency_key, if_match)

    # Prospects

    def get_demand(self, *, days=None, ta=None, limit=None, idempotency_key=None, if_match=None):
        """Demand for your products."""
        return self.request('GET', '/demand', {'days': days, 'ta': ta, 'limit': limit}, None, idempotency_key, if_match)

    # Verification management

    def get_facility(self, id, *, idempotency_key=None, if_match=None):
        """Get a facility."""
        return self.request('GET', '/facilities/' + _q(id), None, None, idempotency_key, if_match)

    # Signing in

    def get_me(self, *, idempotency_key=None, if_match=None):
        """Who this token is."""
        return self.request('GET', '/me', None, None, idempotency_key, if_match)

    # Prospects

    def get_opportunity(self, id, *, idempotency_key=None, if_match=None):
        """Get an opportunity."""
        return self.request('GET', '/opportunities/' + _q(id), None, None, idempotency_key, if_match)

    # Catalog

    def get_product(self, id, *, idempotency_key=None, if_match=None):
        """Get a listed product."""
        return self.request('GET', '/catalog/products/' + _q(id), None, None, idempotency_key, if_match)

    # Verification management

    def get_ta_brand(self, number, *, idempotency_key=None, if_match=None):
        """Get a brand."""
        return self.request('GET', '/brands/' + _q(number), None, None, idempotency_key, if_match)

    def get_ta_product(self, id, *, idempotency_key=None, if_match=None):
        """Get a product."""
        return self.request('GET', '/products/' + _q(id), None, None, idempotency_key, if_match)

    def get_verification(self, ref, *, idempotency_key=None, if_match=None):
        """Get a verification."""
        return self.request('GET', '/verifications/' + _q(ref), None, None, idempotency_key, if_match)

    def get_verification_facility(self, ref, facility_id, *, idempotency_key=None, if_match=None):
        """Get one facility on a verification."""
        return self.request('GET', '/verifications/' + _q(ref) + '/facilities/' + _q(facility_id), None, None, idempotency_key, if_match)

    # Webhooks

    def get_webhook(self, id, *, idempotency_key=None, if_match=None):
        """Get a webhook."""
        return self.request('GET', '/webhooks/' + _q(id), None, None, idempotency_key, if_match)

    # Writes

    def hold_all_for_client(self, id, body, *, idempotency_key=None, if_match=None):
        """Put all a client's products on hold."""
        return self.request('POST', '/clients/' + _q(id) + '/hold-all', None, body, idempotency_key, if_match)

    def hold_verification(self, ref, body, *, idempotency_key=None, if_match=None):
        """Put a verification on hold."""
        return self.request('POST', '/verifications/' + _q(ref) + '/hold', None, body, idempotency_key, if_match)

    # Catalog

    def list_brands(self, *, q=None, limit=None, cursor=None, idempotency_key=None, if_match=None):
        """List brands with listed products."""
        return self.request('GET', '/catalog/brands', {'q': q, 'limit': limit, 'cursor': cursor}, None, idempotency_key, if_match)

    def list_categories(self, *, idempotency_key=None, if_match=None):
        """List categories by certificate group."""
        return self.request('GET', '/catalog/categories', None, None, idempotency_key, if_match)

    # Verification management

    def list_certificates(self, *, program=None, listed=None, updated_since=None, ta=None, limit=None, cursor=None, idempotency_key=None, if_match=None):
        """List your certificates."""
        return self.request('GET', '/certificates', {'program': program, 'listed': listed, 'updated_since': updated_since, 'ta': ta, 'limit': limit, 'cursor': cursor}, None, idempotency_key, if_match)

    # Catalog

    def list_changes(self, *, since=None, limit=None, idempotency_key=None, if_match=None):
        """What changed in the listing."""
        return self.request('GET', '/catalog/changes', {'since': since, 'limit': limit}, None, idempotency_key, if_match)

    # Verification management

    def list_clients(self, *, status=None, program=None, q=None, updated_since=None, ta=None, limit=None, cursor=None, idempotency_key=None, if_match=None):
        """List your clients."""
        return self.request('GET', '/clients', {'status': status, 'program': program, 'q': q, 'updated_since': updated_since, 'ta': ta, 'limit': limit, 'cursor': cursor}, None, idempotency_key, if_match)

    def list_countries_of_origin(self, id, *, idempotency_key=None, if_match=None):
        """List a product's countries of origin."""
        return self.request('GET', '/products/' + _q(id) + '/countries-of-origin', None, None, idempotency_key, if_match)

    # Webhooks

    def list_deliveries(self, id, *, limit=None, idempotency_key=None, if_match=None):
        """List a webhook's deliveries."""
        return self.request('GET', '/webhooks/' + _q(id) + '/deliveries', {'limit': limit}, None, idempotency_key, if_match)

    def list_event_types(self, *, idempotency_key=None, if_match=None):
        """List event types."""
        return self.request('GET', '/event-types', None, None, idempotency_key, if_match)

    # Verification management

    def list_facilities(self, *, q=None, ta=None, limit=None, cursor=None, idempotency_key=None, if_match=None):
        """List facilities on your verifications."""
        return self.request('GET', '/facilities', {'q': q, 'ta': ta, 'limit': limit, 'cursor': cursor}, None, idempotency_key, if_match)

    # Prospects

    def list_opportunities(self, *, stage=None, updated_since=None, ta=None, limit=None, cursor=None, idempotency_key=None, if_match=None):
        """List opportunities shared with you."""
        return self.request('GET', '/opportunities', {'stage': stage, 'updated_since': updated_since, 'ta': ta, 'limit': limit, 'cursor': cursor}, None, idempotency_key, if_match)

    # Verification management

    def list_packages(self, id, *, include_removed=None, idempotency_key=None, if_match=None):
        """List a product's packages."""
        return self.request('GET', '/products/' + _q(id) + '/packages', {'include_removed': include_removed}, None, idempotency_key, if_match)

    # Catalog

    def list_products(self, *, q=None, gtin=None, certificate=None, brand_number=None, program=None, category=None, updated_since=None, limit=None, cursor=None, idempotency_key=None, if_match=None):
        """List and search listed products."""
        return self.request('GET', '/catalog/products', {'q': q, 'gtin': gtin, 'certificate': certificate, 'brand_number': brand_number, 'program': program, 'category': category, 'updated_since': updated_since, 'limit': limit, 'cursor': cursor}, None, idempotency_key, if_match)

    # Verification management

    def list_ta_brands(self, *, q=None, ta=None, limit=None, cursor=None, idempotency_key=None, if_match=None):
        """List brands on your products."""
        return self.request('GET', '/brands', {'q': q, 'ta': ta, 'limit': limit, 'cursor': cursor}, None, idempotency_key, if_match)

    def list_ta_products(self, *, q=None, brand_number=None, participant=None, program=None, updated_since=None, ta=None, limit=None, cursor=None, idempotency_key=None, if_match=None):
        """List your products."""
        return self.request('GET', '/products', {'q': q, 'brand_number': brand_number, 'participant': participant, 'program': program, 'updated_since': updated_since, 'ta': ta, 'limit': limit, 'cursor': cursor}, None, idempotency_key, if_match)

    def list_verification_facilities(self, ref, *, idempotency_key=None, if_match=None):
        """List a verification's facilities."""
        return self.request('GET', '/verifications/' + _q(ref) + '/facilities', None, None, idempotency_key, if_match)

    def list_verifications(self, *, status=None, program=None, product=None, current=None, updated_since=None, ta=None, limit=None, cursor=None, idempotency_key=None, if_match=None):
        """List your verifications."""
        return self.request('GET', '/verifications', {'status': status, 'program': program, 'product': product, 'current': current, 'updated_since': updated_since, 'ta': ta, 'limit': limit, 'cursor': cursor}, None, idempotency_key, if_match)

    # Webhooks

    def list_webhooks(self, *, idempotency_key=None, if_match=None):
        """List your webhooks."""
        return self.request('GET', '/webhooks', None, None, idempotency_key, if_match)

    # Catalog

    def lookup(self, *, gtin=None, certificate=None, product=None, idempotency_key=None, if_match=None):
        """Verify a product."""
        return self.request('GET', '/catalog/lookup', {'gtin': gtin, 'certificate': certificate, 'product': product}, None, idempotency_key, if_match)

    # Writes

    def patch_client(self, id, body, *, idempotency_key=None, if_match=None):
        """Change a client."""
        return self.request('PATCH', '/clients/' + _q(id), None, body, idempotency_key, if_match)

    def patch_package(self, id, body, *, idempotency_key=None, if_match=None):
        """Change a package."""
        return self.request('PATCH', '/packages/' + _q(id), None, body, idempotency_key, if_match)

    def patch_product(self, id, body, *, idempotency_key=None, if_match=None):
        """Change a product."""
        return self.request('PATCH', '/products/' + _q(id), None, body, idempotency_key, if_match)

    def patch_verification(self, ref, body, *, idempotency_key=None, if_match=None):
        """Change a verification's details."""
        return self.request('PATCH', '/verifications/' + _q(ref), None, body, idempotency_key, if_match)

    def put_client_contact(self, id, role, body, *, idempotency_key=None, if_match=None):
        """Set a client's contact."""
        return self.request('PUT', '/clients/' + _q(id) + '/contacts/' + _q(role), None, body, idempotency_key, if_match)

    def put_product(self, id, body, *, idempotency_key=None, if_match=None):
        """Add or update a product."""
        return self.request('PUT', '/products/' + _q(id), None, body, idempotency_key, if_match)

    def put_verification_facility(self, ref, facility_id, body, *, idempotency_key=None, if_match=None):
        """Add or update a facility on a verification."""
        return self.request('PUT', '/verifications/' + _q(ref) + '/facilities/' + _q(facility_id), None, body, idempotency_key, if_match)

    def release_verification(self, ref, *, idempotency_key=None, if_match=None):
        """Release a verification from hold."""
        return self.request('POST', '/verifications/' + _q(ref) + '/release', None, None, idempotency_key, if_match)

    def remove_client_contact(self, id, role, *, idempotency_key=None, if_match=None):
        """Clear a client's contact."""
        return self.request('DELETE', '/clients/' + _q(id) + '/contacts/' + _q(role), None, None, idempotency_key, if_match)

    def remove_package(self, id, *, reason=None, idempotency_key=None, if_match=None):
        """Remove a package."""
        return self.request('DELETE', '/packages/' + _q(id), {'reason': reason}, None, idempotency_key, if_match)

    def remove_verification_facility(self, ref, facility_id, *, idempotency_key=None, if_match=None):
        """Remove a facility from a verification."""
        return self.request('DELETE', '/verifications/' + _q(ref) + '/facilities/' + _q(facility_id), None, None, idempotency_key, if_match)

    def renew_verification(self, ref, body, *, idempotency_key=None, if_match=None):
        """Renew a verification."""
        return self.request('POST', '/verifications/' + _q(ref) + '/renew', None, body, idempotency_key, if_match)

    # Webhooks

    def replay_delivery(self, id, delivery, *, idempotency_key=None, if_match=None):
        """Send a delivery again."""
        return self.request('POST', '/webhooks/' + _q(id) + '/deliveries/' + _q(delivery) + '/replay', None, None, idempotency_key, if_match)

    def rotate_webhook_secret(self, id, *, idempotency_key=None, if_match=None):
        """Rotate a webhook's secret."""
        return self.request('POST', '/webhooks/' + _q(id) + '/rotate-secret', None, None, idempotency_key, if_match)

    # Writes

    def terminate_verification(self, ref, body, *, idempotency_key=None, if_match=None):
        """Terminate a verification."""
        return self.request('POST', '/verifications/' + _q(ref) + '/terminate', None, body, idempotency_key, if_match)

    # Webhooks

    def test_webhook(self, id, *, idempotency_key=None, if_match=None):
        """Send a test."""
        return self.request('POST', '/webhooks/' + _q(id) + '/test', None, None, idempotency_key, if_match)

    # Writes

    def transfer_verification(self, ref, body, *, idempotency_key=None, if_match=None):
        """Mark a verification transferred."""
        return self.request('POST', '/verifications/' + _q(ref) + '/transfer', None, body, idempotency_key, if_match)

    def withdraw_all_for_client(self, id, body, *, idempotency_key=None, if_match=None):
        """Withdraw all a client's products."""
        return self.request('POST', '/clients/' + _q(id) + '/withdraw-all', None, body, idempotency_key, if_match)

    def withdraw_client(self, id, body, *, idempotency_key=None, if_match=None):
        """Withdraw a client."""
        return self.request('POST', '/clients/' + _q(id) + '/withdraw', None, body, idempotency_key, if_match)

    def withdraw_verification(self, ref, body, *, idempotency_key=None, if_match=None):
        """Withdraw a verification."""
        return self.request('POST', '/verifications/' + _q(ref) + '/withdraw', None, body, idempotency_key, if_match)
