
try:
    import urllib2
except:
    import urllib.request as urllib2
import urllib
import json
from saas_deploy.cluster.addr import CLM_ADDR


def get_used_slots(service, ctype):
    req_str = 'using_slots?service=' + service + '&ctype=' + ctype
    return get_data(req_str, timeout=30)


def get_nanny_files(ctype, service_type):
    req_str = 'get_files?ctype=' + ctype + '&service_type=' + service_type
    return get_data(req_str, timeout=40)


def get_export_errors(service, ctype):
    url = CLM_ADDR + 'broadcast?command=get_info_server&ctype='+ ctype +'&service=indexerproxy'
    ans = {}
    try:
        req = urllib2.urlopen(url, timeout=20)
        answ = req.read().decode('utf-8', errors='ignore')
        if 'Resource temporarily unavailable' in answ:
            answ = urllib2.urlopen(url, timeout=20).read().decode('utf-8', errors='ignore')
        ans = json.loads(answ)
    except urllib2.HTTPError as e:
        raise e
    ans = ans.get('indexerproxy', {})
    res = {}
    for host in ans:
        text_host = ans[host].get('text')
        try:
            res[host] = json.loads(text_host).get('result', {}).get('export_replies', {})
        except Exception:
            res[host] = ans[host].get('result', {}).get('export_replies', {})
            # raise e
    return {'result': res}


def get_docfetcher_errors(service, ctype):
    url = CLM_ADDR + '/broadcast?command=get_docfetcher_status&ctype=' + ctype + '&service=' + service
    ans = {}
    try:
        req = urllib2.urlopen(url, timeout=15)
        answ = req.read().decode('utf-8', errors='ignore')
        ans = json.loads(answ)
    except Exception as e:
        return {'result': {}, 'error': e}
    res = {}
    for serv, rs in ans.items():
        res[serv] = {}
        for host in rs:
            for stream, er_stream in rs[host].get('replies', {}).items():
                if er_stream:
                    res[serv][host + '-' + stream] = er_stream
    return {'result': res}


def get_root(service):
    if service in ('common', 'defaults'):
        return '/'
    else:
        return 'configs/'


def send_post_data(req_str, data, timeout, try_json=True, dm_addr=''):
    try:
        addr = dm_addr if dm_addr else CLM_ADDR
        if not addr.startswith('http'):
            addr = 'http://' + addr
        if not addr.endswith('/'):
            addr += '/'
        req = urllib2.urlopen(addr + req_str, data=data, timeout=timeout)
        ans = req.read()
        if try_json:
            try:
                ans = json.loads(ans)
            except:
                ans = ans.decode('utf-8')
        else:
            ans = ans.decode('utf-8')
        return {'success': True, 'ans': ans}
    except urllib2.HTTPError as e:
        ans = e.read()
        if try_json:
            try:
                ans = json.loads(ans)
            except:
                ans = str(ans)
        code = e.code
        return {'success': False, 'ans': ans, 'error': 'httperror', 'code': code}
    except Exception as e:
        return {'success': False, 'ans': '', 'error': 'error %s' % e}


def get_data(req_str, timeout, must_json=True, binary=False):
    ans = ''
    try:
        req = urllib2.urlopen(CLM_ADDR + req_str, timeout=timeout)
        ans = req.read()
        if must_json:
            ansJs = json.loads(ans)
        elif binary:
            ansJs = ans
        else:
            ansJs = ans.decode('utf-8')
        return {'success': True, 'ans': ansJs}
    except urllib2.HTTPError as e:
        ans = e.read()
        try:
            ans = json.loads(ans)
        except:
            ans = str(ans)
        code = e.code
        return {'success': False, 'ans': ans, 'error': 'httperror', 'code': code}
    except Exception as e:
        return {'success': False, 'ans': str(ans), 'error': 'error %s' % e}


def get_storage_value(path, version=-1):
    req_str = 'process_storage?action=get&path=' + path
    if int(version) >= 0:
        req_str += '&version=' + str(version)
    return get_data(req_str, timeout=25)


def set_storage_value(filename, fcontent, login, hex=False):
    req_str = 'set_conf?service=&root=/&login=' + login + '&filename=' + filename
    if hex:
        req_str += '&hex=yes'
    return send_post_data(req_str, data=fcontent, timeout=25)


def fetch_fml_formula(formula_fml_id, filename, filepath, login):
    req_str = 'fetch_fml_formula?login=' + login + \
        '&fml_id=' + formula_fml_id + '&target_name=' + filename + '&path=' + filepath
    return get_data(req_str, timeout=30, must_json=False)


def get_history(path):
    req_str = 'process_storage?action=get&path=/history/' + path.strip('/')
    return get_data(req_str, timeout=40)


def fml_convert(service, action, content, dm_addr=''):
    req_str = 'fml_ops?action=' + action + '&service=' + service
    return send_post_data(req_str, content, timeout=30, try_json=False, dm_addr=dm_addr)


def set_sla(service, ctype, fields, login, is_patch=True):
    action = 'patch' if is_patch else 'set'
    req_str = 'process_sla_description?action=' + action + '&login=' + login + '&service=' + service + '&ctype=' + ctype
    return send_post_data(req_str, json.dumps(fields).encode('ascii'), timeout=30)


def get_cluster_map(service, ctype, service_type='rtyserver'):
    if service in ('searchproxy', 'indexerproxy', 'deploy_manager'):
        service_type = service
    req_str = 'get_cluster_map?ctype=' + ctype + '&service=' + service + '&service_type=' + service_type
    return get_data(req_str, timeout=30)


def edit_cluster_map(service, ctype, service_type, service_info):
    if service in ('searchproxy', 'indexerproxy', 'deploy_manager'):
        service_type = service
    req_str = 'modify_searchmap?action=edit_service&ctype=' + ctype + '&service=' + service + '&service_type=' + service_type\
              + '&service_info=' + service_info
    return get_data(req_str, timeout=40, must_json=False)


def secret_key(service):
    req_str = 'secret_key?service=' + service
    return get_data(req_str, timeout=30)


def get_conf(service, fname, version=-1, ignore_absent=False):
    ans = ''
    root = get_root(service)
    try:
        req_str = 'process_storage?action=get&path=' + root + service + '/' + fname
        if int(version) >= 0:
            req_str += '&version=' + str(version)
        req = urllib2.urlopen(CLM_ADDR + req_str, timeout=10)
        ans = req.read()
    except urllib2.HTTPError as e:
        if ignore_absent and e.code < 500:
            return '', ''
        else:
            return '', 'httperror %s' % e
    except Exception as e:
        return '', 'error %s' % e
    try:
        res = json.loads(ans)['data']
    except Exception as e:
        return '', 'error: %s' % e
    return res, ''


def download_conf(path):
    url = 'process_storage?action=get&download=yes&path=' + path
    return urllib2.urlopen(CLM_ADDR + url).read()


def remove_conf(path, login):
    url = 'process_storage?action=rm&login=' + login + '&path=' + path
    return get_data(url, timeout=15, must_json=False)


def get_tags_info(service, ctype):
    if service in ('searchproxy', 'indexerproxy', 'deploy_manager'):
        service_type = service
    else:
        service_type = 'rtyserver'
    url = 'modify_tags_info?action=get&service=' + service + '&ctype=' + ctype + '&service_type=' + service_type
    res = get_data(url, timeout=15)
    if res.get('code') == 404:
        res['success'] = True
    return res


def modify_tags_info(params):
    url_req = '&'.join([nm + '=' + v for nm, v in params.items()])
    url = 'modify_tags_info?' + url_req
    res = get_data(url, timeout=30)
    return res


def modify_searchmap(params):
    url_req = '&'.join([nm + '=' + v for nm, v in params.items()])
    url = 'modify_searchmap?' + url_req
    res = get_data(url, timeout=30, must_json=False)
    return res


def cluster_control(params):
    url_req = '&'.join([nm + '=' + v for nm, v in params.items()])
    url = 'cluster_control?' + url_req
    res = get_data(url, timeout=30, must_json=False)
    return res


def add_replica(params):
    url_req = '&'.join([nm + '=' + v for nm, v in params.items()])
    url = 'add_replica?' + url_req
    res = get_data(url, timeout=40, must_json=False)
    return res


def release_slots(params):
    url_req = '&'.join([nm + '=' + v for nm, v in params.items()])
    url = 'release_slots?' + url_req
    res = get_data(url, timeout=30, must_json=False)
    return res


def release_endpointsets(params):
    url_req = '&'.join([nm + '=' + v for nm, v in params.items()])
    url = 'release_endpointsets?' + url_req + '&action=release'
    res = get_data(url, timeout=30, must_json=False)
    return res


def cancel_task(task_id):
    url = 'control_task?action=cancel&task_id=' + task_id
    return get_data(url, timeout=30, must_json=False)


def deploy_trace(task_id):
    url = 'deploy_info?id=' + task_id
    return get_data(url, timeout=15)


def deploy_tasks(service, ctype):
    url = 'deploy_info?service=' + service + '&ctype=' + ctype
    return get_data(url, timeout=25)


def build_deploy_command(service, ctype, service_type, dead_percs, slots, diff_only=False, login='unknown'):
    if service_type in ('searchproxy', 'indexerproxy'):
        dt_service = service_type
    else:
        dt_service = service
    url = 'deploy?service=' + dt_service + '&ctype=' + ctype +\
          '&service_type=' + service_type + \
          '&may_be_dead_procentage=' + str(dead_percs) + \
          '&diff_slots_only=' + str(diff_only)
    if slots is not None:
        url += '&slots=' + str(slots)
    if service_type in ('searchproxy', 'indexerproxy') and service != service_type:
        url += '&version=CURRENT'
        url += '&force_services=' + service
    url += '&login=' + login
    return url


def deploy(url):
    return get_data(url, timeout=30, must_json=False)


def is_deploy_blocked(service, ctype):
    url = 'deploy_block?action=get&service=' + service + '&ctype=' + ctype
    return get_data(url, timeout=30, must_json=True)


def deploy_block(service, ctype, author, reason, action):
    url = 'deploy_block?service=' + service + '&ctype=' + ctype + '&action=' + action + '&author=' + author
    if action == 'set':
        try:
            url += '&' + urllib.parse.urlencode({'reason': reason})
        except:
            return {'success': False, 'error': 'cannot encode reason field, use latin please'}
    return get_data(url, timeout=30, must_json=False)


def deploy_trace_old(service, ctype, ascode, fpol='', dpol=''):
    ans, errors = 'error', {}
    try:
        if ascode == 'nocode':
            req_url = CLM_ADDR + 'deploy?service=%s&ctype=%s' % (service, ctype)
            if fpol:
                req_url += '&force_policy=' + fpol
            if dpol:
                req_url += '&deploy_policy=' + dpol
            # raise Exception(req_url)
        else:
            req_url = CLM_ADDR + 'deploy_info?id=' + str(ascode)
        req = urllib2.urlopen(req_url, timeout=20)
        ans = req.read().decode('utf-8')
    except urllib2.HTTPError as e:
        errors['deploy'] = True
        errors['deploy_http_error'] = "%s, %s" % (e.code, str(e.read()))
#    except Exception as e:
#        errors['deploy'] = True
#        errors['deploy_error'] = str(e)

    return ans, errors


def list_deploy_tasks(service):
    tasks, errors = [], {}
    ans = ''
    try:
        req = urllib2.urlopen(CLM_ADDR + 'deploy_info?service=' + service, timeout=10)
        ans = req.read()
    except Exception as e:
        errors['get_deploy_tasks'] = '%s' % e
    try:
        res = json.loads(ans)
        for tid, tsk in res.get(service, {}).items():
            tasks.append(tsk)
    except Exception as e:
        errors['json_tasks'] = 'ans:%s err:%s' % (str(ans), e)

    return tasks, errors


def list_conf(slot, service, ctype, get_last=False):
    url = 'list_conf?'
    url += ('slot=' + slot) if slot else ''
    url += ('&service=' + service) if service else ''
    url += ('&ctype=' + ctype) if ctype else ''
    url += '&last_versions=1' if get_last else ''
    return get_data(url, 20, True)


def get_conf_by_url(file_url):
    url = 'get_conf?filename=' + file_url
    return get_data(url, 15, False)


def get_service_sla(service, ctype):
    req_str = 'process_sla_description?action=get&ctype=' + ctype + '&service=' + service
    return get_data(req_str, timeout=30)


def get_default_alerts_conf():
    conf, err = get_conf('defaults', 'alerts.conf')
    if not err:
        return {'success': True, 'conf': json.loads(conf)}
    return {'success': False, 'error': 'on default conf: %s' % err}


def get_alerts_conf_old(service, diff_only=False):
    conf, err = get_conf(service, 'alerts.conf', ignore_absent=True)
    if err:
        return {'success': False, 'error': 'on getting conf %s' % err}
    conf = json.loads(conf) if conf else {}
    if not diff_only:
        if service in ('default', 'defaults', 'common'):
            return {'success': True, 'conf': conf, 'default_conf': conf}
        dconf = get_default_alerts_conf()
        if not dconf['success']:
            return dconf
        return {'success': True, 'conf': conf, 'default_conf': dconf['conf']}
    else:
        return {'success': True, 'conf': conf}


def set_alerts_conf(service, conf, login='unknown'):
    ans, err = send_post(
        'set_conf?service=' + service + '&filename=alerts.conf&root=' + get_root(service) + '&login=' + login,
         json.dumps(conf, indent=4).encode('ascii'))
    if err:
        return {'success': False, 'errors': err}
    return {'success': True, 'answer': ans}


def get_ctypes(dm_addr=''):
    try:
        req = urllib2.urlopen((dm_addr or CLM_ADDR) + 'ctypes', timeout=20)
        ans = req.read()
    except Exception as e:
        return {'success': False, 'error': '%s' % e}
    try:
        res = json.loads(ans)
    except Exception as e:
        return {'success': False,
                'error': 'cannot parse json: %s' % e,
                'result_raw': str(ans)}
    return {'success': True, 'result': res}


def get_services(ctype='', dm_addr=''):
    servlist = []
    serv_inact = []
    serv_meta = []
    serv_ct = {}
    errors = {}
    try:
        req = urllib2.urlopen((dm_addr or CLM_ADDR) + 'get_services', timeout=10)
        ans = req.read()
    except urllib2.HTTPError as e:
        return [], {}, [], {'get_services http error': '%s' % str(e.read())}
    except Exception as e:
        return [], {}, [], {'get_services_error': '%s' % e}

    serv = json.loads(ans)
    if len(serv) == 0:
        errors['empty services list'] = True

    slots = {}

    for gr, sgr in serv.items():
        if isinstance(sgr, dict):
            srt = sgr.get('rtyserver', {})
            if '' in srt:
                del srt['']
            if gr != 'unused' and (not ctype or ctype == gr):
                servlist.extend(srt.keys())
                serv_ct[gr] = srt.keys()
                slots[gr] = []
                try:
                    for srv in srt:
                        slots[gr].extend([{'slot': slot, 'service': srv} for slot in srt[srv]])
                    slots[gr] = sorted(slots[gr][:], key=lambda x: x['slot'])
                except Exception as e:
                    pass
                    errors['parse_serv_slots'] = '%s' % e
            else:
                serv_inact = [s for s in srt.keys() if s not in ('searchproxy', 'indexerproxy')]
            srm = sgr.get('metaservice', {})
            if '' in srm:
                del srm['']
            if gr != 'unused':
                servlist.extend(srm.keys())
                serv_ct[gr] = list(serv_ct.get(gr, [])) + list(srm.keys())
                serv_meta.extend(srm.keys())
        else:
            if gr != 'unused':
                servlist.extend(serv[gr])
            else:
                serv_inact = [s for s in serv[gr] if s not in ('searchproxy', 'indexerproxy')]

    servlist = sorted(list(set(servlist)))
    serv_meta = sorted(list(set(serv_meta)))
    # servi = json.loads(ans1)
    # serv_inact = [s for s in servi.get('dirs') if not s in servlist and not s in ('searchproxy', 'indexerproxy')]
    serv_ct['inactive'] = serv_inact
    serv_ct['metaservices'] = serv_meta

    return servlist, serv_ct, slots, errors


def get_service_cluster_info(service, ctype, fields=''):
    req_str = 'api/slots_by_interval/' + service + '?ctype=' + ctype + '&service=' + service + '&service_type=rtyserver'
    req_str += '&filter=replics_consistance,invalid_config_files,result.server_status_global.state,result.server_status_global.info,result.search_enabled,result.indexing_enabled'
    req_str += ',result.search_rps_neh.30,result.search_rps_http.30,'
    req_str += fields
    return get_data(req_str, timeout=40)


def get_total_cluster_info(service, ctype, filters='neighbors', fields=''):
    service_type = 'rtyserver'
    if service in ('searchproxy', 'indexerproxy', 'deploy_manager'):
        service_type = service
    req_str = 'dashboard?ctype=' + ctype + '&service=' + service + '&service_type=' + service_type + '&command=get_info_server'
    req_str += '&min_ok_uptime=300s'
    req_str += '&slots_filters=' + filters
    req_str += '&filter=result.controller_status,result.server_status_global.state,result.controller_uptime,result.mem_size_real,result.cpu_load_user,result.cpu_load_system,'
    req_str += 'id,result.slot.container.constraints.cpu_policy,result.slot.container.constraints.cpu_limit,result.slot.container.constraints.cpu_guarantee,'
    req_str += 'result.config.DaemonConfig.[0].Controller.[0].DMOptions.[0].CType,result.config.DaemonConfig.[0].Controller.[0].DMOptions.[0].ServiceType,'
    req_str += 'result.slot.container.constraints.memory_limit'
    req_str += '&groupings=$datacenter$;host(result.load_average,result.cpu_count,result.total_mem_size)'
    return get_data(req_str, timeout=60)


def cluster_proxies_state(ctype, proxy_kind):
    req_str = 'dashboard?command=get_status&' \
              'service=' + proxy_kind + '&service_type=' + proxy_kind + '&ctype=' + ctype + \
              '&groupings=$datacenter_alias$(result.controller_status@facet)&report_slots=no&flat_report=no'
    return get_data(req_str, timeout=30)


def get_iproxy_queues(ctype):
    req_str = 'dashboard?command=get_info_server&service=indexerproxy&service_type=indexerproxy' \
              '&ctype=' + ctype + \
              '&filter=host,result.queues' \
              '&groupings=service_to(docs@summ,space_bytes@summ);backend&categories=@indexerproxy.slot.@result.@queues.service_to.backend&report_slots=no&flat_report=no'
    return get_data(req_str, timeout=30)


def get_repshards_state(ctype, by_what, skip_eps):
    if by_what == 'replicas':
        by_part = 'service;\\$datacenter_alias\\$;replic_id(result.controller_status@facet)'
    elif by_what == 'shards':
        by_part = 'service;interval(result.controller_status@facet)'
    else:
        return {'success': False, 'error': 'unknown by_what value %s' % by_what}
    req_str = 'dashboard?command=get_status&service=*&service_type=rtyserver' \
              '&ctype=' + ctype + '&groupings=' + by_part + '&skip_eps=' + str(skip_eps) + \
              '&report_slots=no&flat_report=no&report_flagged=1'
    return get_data(req_str, timeout=40)


def get_services_sizes(ctype, addr=''):
    url = '/dashboard?command=get_info_server&service=*&service_type=rtyserver&ctype=' + ctype \
          +'&groupings=service(result.files_size.__SUM@summ,docs_in_replica@summ,replic_id@facet);' \
          + 'interval@(docs_in_replica=result.docs_in_final_indexes%2Bresult.docs_in_disk_indexers@average)'\
          +'&flat_report=no'
    try:
        req = urllib2.urlopen((addr or CLM_ADDR) + url, timeout=10)
        ans = req.read()
    except Exception as e:
        return {'error': '%s' % e}
    try:
        res = json.loads(ans)
    except Exception as e:
        return {'error': '%s, ans=%s' % (e, str(ans))}
    return {'result': res}


def send_post(request, content):
    ans = 'no answer'
    errors = {}
    req = urllib2.Request(CLM_ADDR + str(request))

    try:
        if content:
            req.add_header('Content-Length', len(content))
            resp = urllib2.urlopen(req, content, timeout=10)
        else:
            resp = urllib2.urlopen(req, timeout=10)
        ans = resp.read()
        ans = ans.decode('utf-8')
    except urllib2.HTTPError as e:
        errors['http'] = '%s' % e
        errors['answer'] = str(e.read())
        errors['request'] = req
    except Exception as e:
        errors['while_send'] = '%s' % e

    return ans, errors
