Runs some basic checks to ensure a VAT ID looks properly formatted. If so,
    checks it against the VIES system for EU VAT IDs or data.brreg.no for
    Norwegian VAT ID.

    :param vat_id:
        The VAT ID to check. Allows "GR" prefix for Greece, even though it
        should be "EL".

    :raises:
        ValueError - If the is not a string or is not in the format of two characters plus an identifier
        InvalidError - If the VAT ID is not valid
        WebServiceUnavailableError - If the VIES VAT ID service is unable to process the request - this is fairly common
        WebServiceError - If there was an error parsing the response from the server - usually this means something changed in the webservice
        urllib.error.URLError/urllib2.URLError - If there is an issue communicating with VIES or data.brreg.no

    :return:
        None if the VAT ID is blank or not for an EU country or Norway
        A tuple of (two-character country code, normalized VAT id, company name) if valid

def validate(vat_id):
    """
    Runs some basic checks to ensure a VAT ID looks properly formatted. If so,
    checks it against the VIES system for EU VAT IDs or data.brreg.no for
    Norwegian VAT ID.

    :param vat_id:
        The VAT ID to check. Allows "GR" prefix for Greece, even though it
        should be "EL".

    :raises:
        ValueError - If the is not a string or is not in the format of two characters plus an identifier
        InvalidError - If the VAT ID is not valid
        WebServiceUnavailableError - If the VIES VAT ID service is unable to process the request - this is fairly common
        WebServiceError - If there was an error parsing the response from the server - usually this means something changed in the webservice
        urllib.error.URLError/urllib2.URLError - If there is an issue communicating with VIES or data.brreg.no

    :return:
        None if the VAT ID is blank or not for an EU country or Norway
        A tuple of (two-character country code, normalized VAT id, company name) if valid
    """

    vat_id = normalize(vat_id)

    if not vat_id:
        return vat_id

    country_prefix = vat_id[0:2]

    number = vat_id[2:]

    if not re.match(ID_PATTERNS[country_prefix]['regex'], number):
        raise InvalidError('VAT ID does not appear to be properly formatted for %s' % country_prefix)

    if country_prefix == 'NO':
        organization_number = number.replace('MVA', '')
        validation_url = 'http://data.brreg.no/enhetsregisteret/enhet/%s.json' % organization_number

        try:
            response = urlopen(validation_url)
            _, params = cgi.parse_header(response.headers['Content-Type'])
            if 'charset' in params:
                encoding = params['charset']
            else:
                encoding = 'utf-8'

            return_json = response.read().decode(encoding)

            # Example response:
            #
            # {
            #     "organisasjonsnummer": 974760673,
            #     "navn": "REGISTERENHETEN I BRØNNØYSUND",
            #     "registreringsdatoEnhetsregisteret": "1995-08-09",
            #     "organisasjonsform": "ORGL",
            #     "hjemmeside": "www.brreg.no",
            #     "registrertIFrivillighetsregisteret": "N",
            #     "registrertIMvaregisteret": "N",
            #     "registrertIForetaksregisteret": "N",
            #     "registrertIStiftelsesregisteret": "N",
            #     "antallAnsatte": 562,
            #     "institusjonellSektorkode": {
            #         "kode": "6100",
            #         "beskrivelse": "Statsforvaltningen"
            #     },
            #     "naeringskode1": {
            #         "kode": "84.110",
            #         "beskrivelse": "Generell offentlig administrasjon"
            #     },
            #     "postadresse": {
            #         "adresse": "Postboks 900",
            #         "postnummer": "8910",
            #         "poststed": "BRØNNØYSUND",
            #         "kommunenummer": "1813",
            #         "kommune": "BRØNNØY",
            #         "landkode": "NO",
            #         "land": "Norge"
            #     },
            #     "forretningsadresse": {
            #         "adresse": "Havnegata 48",
            #         "postnummer": "8900",
            #         "poststed": "BRØNNØYSUND",
            #         "kommunenummer": "1813",
            #         "kommune": "BRØNNØY",
            #         "landkode": "NO",
            #         "land": "Norge"
            #     },
            #     "konkurs": "N",
            #     "underAvvikling": "N",
            #     "underTvangsavviklingEllerTvangsopplosning": "N",
            #     "overordnetEnhet": 912660680,
            #     "links": [
            #         {
            #             "rel": "self",
            #             "href": "http://data.brreg.no/enhetsregisteret/enhet/974760673"
            #         },
            #         {
            #             "rel": "overordnetEnhet",
            #             "href": "http://data.brreg.no/enhetsregisteret/enhet/912660680"
            #         }
            #     ]
            # }

            info = json.loads(return_json)

            # This should never happen, but keeping it incase the API is changed
            if 'organisasjonsnummer' not in info or info['organisasjonsnummer'] != int(organization_number):
                raise WebServiceError('No or different value for the "organisasjonsnummer" key in response from data.brreg.no')

            company_name = info['organisasjonsnummer']

        except (HTTPError) as e:
            # If a number is invalid, we get a 404
            if e.code == 404:
                raise InvalidError('VAT ID is invalid')

            # If we get anything but a 404 we want the exception to be recorded
            raise

    # EU countries
    else:
        post_data = '''
            <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:ec.europa.eu:taxud:vies:services:checkVat:types">
               <soapenv:Header/>
               <soapenv:Body>
                  <urn:checkVat>
                     <urn:countryCode>%s</urn:countryCode>
                     <urn:vatNumber>%s</urn:vatNumber>
                  </urn:checkVat>
               </soapenv:Body>
            </soapenv:Envelope>
        ''' % (country_prefix, number)

        request = Request('http://ec.europa.eu/taxation_customs/vies/services/checkVatService')
        request.add_header('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8')

        try:
            response = urlopen(request, post_data.encode('utf-8'))
        except (HTTPError) as e:
            # If one of the country VAT ID services is down, we get a 500
            if e.code == 500:
                raise WebServiceUnavailableError('VAT ID validation is not currently available')

            # If we get anything but a 500 we want the exception to be recorded
            raise

        _, params = cgi.parse_header(response.headers['Content-Type'])
        if 'charset' in params:
            encoding = params['charset']
        else:
            encoding = 'utf-8'

        return_xml = response.read().decode(encoding)

        # Example response:
        #
        # <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
        #    <soap:Body>
        #       <checkVatResponse xmlns="urn:ec.europa.eu:taxud:vies:services:checkVat:types">
        #          <countryCode>GB</countryCode>
        #          <vatNumber>GD001</vatNumber>
        #          <requestDate>2014-12-17+01:00</requestDate>
        #          <valid>true</valid>
        #          <name>MINISTRY OF AGRICULTURE FISHERIES &amp; FOOD</name>
        #          <address>ROOM C206
        # GOVERNMENT BUILDINGS
        # EPSOM ROAD
        # GUILDFORD
        # SURREY
        # GU1 2LD</address>
        #       </checkVatResponse>
        #    </soap:Body>
        # </soap:Envelope>

        try:
            # If we don't explicitly recode to UTF-8, ElementTree stupidly uses
            # ascii on Python 2.7
            envelope = ElementTree.fromstring(return_xml.encode('utf-8'))
        except (ElementTree.ParseError):
            raise WebServiceError('Unable to parse response from VIES')

        namespaces = {
            'soap': 'http://schemas.xmlsoap.org/soap/envelope/',
            'vat':  'urn:ec.europa.eu:taxud:vies:services:checkVat:types'
        }
        valid_elements = envelope.findall('./soap:Body/vat:checkVatResponse/vat:valid', namespaces)
        if not valid_elements:
            # Fail loudly if the XML seems to have changed
            raise WebServiceError('Unable to find <valid> tag in response from VIES')

        name_elements = envelope.findall('./soap:Body/vat:checkVatResponse/vat:name', namespaces)
        if not name_elements:
            # Fail loudly if the XML seems to have changed
            raise WebServiceError('Unable to find <name> tag in response from VIES')

        if valid_elements[0].text.lower() != 'true':
            raise InvalidError('VAT ID is invalid')

        company_name = name_elements[0].text

    return (ID_PATTERNS[country_prefix]['country_code'], vat_id, company_name)