#!/usr/bin/env python3

import sys
import time
import requests
import xml.etree.ElementTree

API_HOST = 'api.evpsys.com'
API_VERSION = '1.3'
API_TOKEN = '{PUT-YOUR-TOKEN-HERE}'
API_KEY = '{PUT-YOUR-KEY-HERE}'
API_ACTOR_EMAIL = 'jane.doe@example.com'
API_ACTOR_WINDOWS_USER = None
LOCAL_PORTFOLIO_ID = 'joy-demo-portfolio-001'
LOT_IDS = {
    1: 'joy-demo-lot-001',
    2: 'joy-demo-lot-002',
    3: 'joy-demo-lot-003',
    4: 'joy-demo-lot-004',
}
INITIAL_REPORT_FILE_NAME = 'joy-demo-initial-report'
REVISED_REPORT_FILE_NAME = 'joy-demo-revised-report'


def get_document(text):
    document = xml.etree.ElementTree.fromstring(text)
    if document.find('status').text not in ['ok', 'pending']:
        raise Exception(document.find('error-text').text)
    return document


def get_url(path):
    path = path if path.startswith('/') else '/' + path
    return 'https://%s/%s%s%s' % (API_HOST, API_VERSION, path, '&' if '?' in path else '?') + 'output=xml'


def expect(condition, message):
    if not condition:
        raise Exception(message)


def verify_portfolio_xvf(portfolio_id, expected_portfolio_id, expected_lot_ids):
    response = requests.get(get_url('/portfolio/%s' % portfolio_id), auth=auth, headers=headers)
    response.raise_for_status()
    xvf = xml.etree.ElementTree.fromstring(response.text)
    portfolio = xvf.find('portfolio')
    expect(xvf.attrib.get('version') == '1.20', 'Stored XVF version should be 1.20')
    expect(portfolio is not None, 'Stored XVF is missing portfolio element')
    expect(portfolio.attrib.get('portfolioid') == expected_portfolio_id,
           'Stored portfolioid mismatch: %r' % portfolio.attrib.get('portfolioid'))
    expect(portfolio.attrib.get('api-portfolioid') == portfolio_id,
           'Stored api-portfolioid mismatch: %r' % portfolio.attrib.get('api-portfolioid'))
    lots = {}
    for lot in portfolio.findall('lot'):
        lots[int(lot.attrib['number'])] = lot
    for lot_number, lot_id in expected_lot_ids.items():
        expect(lot_number in lots, 'Stored XVF is missing lot %d' % lot_number)
        expect(lots[lot_number].attrib.get('lotid') == lot_id,
               'Stored lotid mismatch for lot %d: %r' % (lot_number, lots[lot_number].attrib.get('lotid')))
    return xvf


# Build the auth
auth = (API_TOKEN, API_KEY)
headers = {
    'X-EVP-Actor-Email': API_ACTOR_EMAIL,
}
if API_ACTOR_WINDOWS_USER:
    headers['X-EVP-Actor-Windows-User'] = API_ACTOR_WINDOWS_USER

# Create the portfolio -------------------------------------------------------
portfolio = """
    <input version="1.2">
     <portfolio death-date="20180303" distribution-date="20180403" appraisal-date="20180404" name="John Doe" account="Account #123"
       title-1="Estate of John Doe" portfolioid="joy-demo-portfolio-001" report-file-name="joy-demo-default-file-id">
      <lot number="1" lotid="joy-demo-lot-001" identifier="IBM" shares="123.456" />
      <lot number="2" lotid="joy-demo-lot-002" identifier="MSFT" shares="789" />
      <lot number="4" lotid="joy-demo-lot-004" identifier="AMZN" shares="9876.54321" />
     </portfolio>
    </input>
"""
print('Creating the portfolio...')
try:
    response = requests.post(get_url('/portfolio'), auth=auth, headers=headers, data=portfolio)
    response.raise_for_status()
except Exception as e:
    print('  FAILED (POST /portfolio error: %s)' % repr(e))
    sys.exit(-1)
print('  Succeeded')

# Get the portfolio-id from the response -------------------------------------
print('Getting the portfolio-id...')
try:
    document = get_document(response.text)
    portfolio_id = document.find('portfolio-id').text
except Exception as e:
    print('  FAILED (POST /portfolio response format error: %s)' % repr(e))
    sys.exit(-1)
print('  Succeeded')

# Verify the stored XVF contains durable portfolio/lot ids -------------------
print('Verifying the stored portfolio ids...')
try:
    verify_portfolio_xvf(portfolio_id, LOCAL_PORTFOLIO_ID, {1: LOT_IDS[1], 2: LOT_IDS[2], 4: LOT_IDS[4]})
except Exception as e:
    print('  FAILED (GET /portfolio/{portfolio-id} identity verification error: %s)' % repr(e))
    sys.exit(-1)
print('  Succeeded')

# Add and modify lots in the portfolio, using the portfolio-id ---------------
print('Updating lots in portfolio...')
portfolio = """
    <input version="1.2">
     <portfolio>
      <lot number="3" lotid="joy-demo-lot-003" identifier="AAPL" shares="123">
       <alt shares="0">
        <action type="Sold" date="20180601" shares="100" />
        <action type="Sold" date="20180602" shares="20" />
        <action type="Sold" date="20180701" identifier="TSLA" shares="321" />
       </alt>
      </lot>
      <lot number="4" identifier="AMZN" shares="1234.5678" />
     </portfolio>
    </input>
"""
try:
    response = requests.put(get_url('/portfolio/%s' % portfolio_id), auth=auth, headers=headers, data=portfolio)
    response.raise_for_status()
except Exception as e:
    print('  FAILED (PUT /portfolio/{portfolio-id} error: %s)' % repr(e))
    sys.exit(-1)
try:
    document = get_document(response.text)
except Exception as e:
    print('  FAILED (PUT /portfolio/{portfolio-id} response format error: %s)' % repr(e))
    sys.exit(-1)
print('  Succeeded')

# Verify lotid preservation when lot number fallback is used -----------------
print('Verifying id preservation after update...')
try:
    verify_portfolio_xvf(portfolio_id, LOCAL_PORTFOLIO_ID, LOT_IDS)
except Exception as e:
    print('  FAILED (lotid preservation verification error: %s)' % repr(e))
    sys.exit(-1)
print('  Succeeded')

# List the existing portfolios -----------------------------------------------
print('Listing the portfolios...')
try:
    response = requests.get(get_url('/portfolio'), auth=auth, headers=headers, data=portfolio)
    response.raise_for_status()
    document = get_document(response.text)
    for portfolio in document.iter('portfolio'):
        print('  Portfolio: %s (ID %s, created %s)' % (portfolio.attrib.get('name', '[Unknown]'), portfolio.attrib['id'],
              portfolio.attrib['portfolio_time_created']))
    if document.find('more').text == 'true':
        print('  ...and more')
except Exception as e:
    print('  FAILED (GET /portfolio error: %s)' % repr(e))
    sys.exit(-1)
print('  Succeeded')

# Start an evaluation of the portfolio, using the portfolio-id ---------------
print('Starting the report...')
try:
    response = requests.post(get_url('/portfolio/%s/estateval/report?report=dod&file-name=%s' % (portfolio_id, INITIAL_REPORT_FILE_NAME)), auth=auth, headers=headers)
    response.raise_for_status()
except Exception as e:
    print('  FAILED (POST /portfolio/{portfolio-id}/estateval/report error: %s)' % repr(e))
    sys.exit(-1)
print('  Succeeded')

# Get the report-id from the response ----------------------------------------
print('Getting the report-id...')
try:
    document = get_document(response.text)
    report_id = document.find('report-id').text
except Exception as e:
    print('  FAILED (POST /portfolio/{portfolio-id}/estateval/report response format error: %s)' % repr(e))
    sys.exit(-1)
print('  Succeeded')

# Poll the report-id for completion ------------------------------------------
print('Polling the report status...')
try:
    while True:
        response = requests.get(get_url('/portfolio/%s/estateval/report/%s/status' % (portfolio_id, report_id)), auth=auth, headers=headers)
        response.raise_for_status()
        document = get_document(response.text)
        if document.find('status').text == 'ok':
            break
        print('  %03d%%: %s' % (int(document.find('pending-percent').text), document.find('pending-text').text))
        time.sleep(1)
except Exception as e:
    print('  FAILED (GET /portfolio/{portfolio-id}/estateval/report/{report-id}/status error: %s)' % repr(e))
    sys.exit(-1)
print('  Succeeded')

# List the existing reports --------------------------------------------------
print('Listing the reports...')
try:
    response = requests.get(get_url('/portfolio/%s/estateval/report' % portfolio_id), auth=auth, headers=headers, data=portfolio)
    response.raise_for_status()
    document = get_document(response.text)
    for report in document.iter('report'):
        print('  Report: %s for %s (ID %s, created %s)' % (report.attrib['type'], report.attrib['death-date'], report.attrib['id'],
              report.attrib['report_time_created']))
    if document.find('more').text == 'true':
        print('  ...and more')
except Exception as e:
    print('  FAILED (GET /portfolio/{portfolio-id}/estateval/report error: %s)' % repr(e))
    sys.exit(-1)
print('  Succeeded')

# Get the report data --------------------------------------------------------
print('Getting the report data...')
try:
    response = requests.get(get_url('/portfolio/%s/estateval/report/%s' % (portfolio_id, report_id)), auth=auth, headers=headers)
    response.raise_for_status()
except Exception as e:
    print('  FAILED (POST /portfolio/{portfolio-id}/estateval/report/{report-id} error: %s)' % repr(e))
    sys.exit(-1)
print('  Succeeded')

print("\n%s" % response.text)

# Remove a lot and compact the remaining lot numbers ------------------------
print('Removing lot 2 and renumbering...')
portfolio = """
    <input version="1.2">
     <portfolio>
      <lot number="2" />
     </portfolio>
    </input>
"""
try:
    response = requests.put(get_url('/portfolio/%s?renumber=yes' % portfolio_id), auth=auth, headers=headers, data=portfolio)
    response.raise_for_status()
    document = get_document(response.text)
except Exception as e:
    print('  FAILED (PUT /portfolio/{portfolio-id}?renumber=yes error: %s)' % repr(e))
    sys.exit(-1)
print('  Succeeded')

print('Verifying renumbered lot order...')
try:
    verify_portfolio_xvf(portfolio_id, LOCAL_PORTFOLIO_ID, {1: LOT_IDS[1], 2: LOT_IDS[3], 3: LOT_IDS[4]})
except Exception as e:
    print('  FAILED (renumber verification error: %s)' % repr(e))
    sys.exit(-1)
print('  Succeeded')

# Start a revised evaluation with a new File ID override --------------------
print('Starting the revised report...')
try:
    response = requests.post(get_url('/portfolio/%s/estateval/report?report=dod&file-name=%s' % (portfolio_id, REVISED_REPORT_FILE_NAME)),
                             auth=auth, headers=headers)
    response.raise_for_status()
except Exception as e:
    print('  FAILED (POST revised /portfolio/{portfolio-id}/estateval/report error: %s)' % repr(e))
    sys.exit(-1)
print('  Succeeded')

print('Getting the revised report-id...')
try:
    document = get_document(response.text)
    report_id = document.find('report-id').text
except Exception as e:
    print('  FAILED (POST revised /portfolio/{portfolio-id}/estateval/report response format error: %s)' % repr(e))
    sys.exit(-1)
print('  Succeeded')

print('Polling the revised report status...')
try:
    while True:
        response = requests.get(get_url('/portfolio/%s/estateval/report/%s/status' % (portfolio_id, report_id)), auth=auth, headers=headers)
        response.raise_for_status()
        document = get_document(response.text)
        if document.find('status').text == 'ok':
            break
        print('  %03d%%: %s' % (int(document.find('pending-percent').text), document.find('pending-text').text))
        time.sleep(1)
except Exception as e:
    print('  FAILED (GET revised /portfolio/{portfolio-id}/estateval/report/{report-id}/status error: %s)' % repr(e))
    sys.exit(-1)
print('  Succeeded')
