import uuid
from fastapi.testclient import TestClient
from test_boundaries import app, client, write, job, quote, invoice, portal, ORIGIN


def contact(c, name='Alex Demo'):
    r=write(c,'/api/customers',{'name':name,'email':'alex@customer.example','phone':'012345','address':'1 Example Lane'})
    assert r.status_code==200,r.text
    return r.json()


def work(c, customer):
    return write(c,'/api/customers/'+customer['id']+'/jobs',{'customer_version':customer['version'],'service':'Another shelf','message':'A second invented job for this customer.'})


def test_explicit_reuse_creates_one_customer_and_many_jobs(app):
    c=client(app);customer=contact(c);a=work(c,customer);b=work(c,customer)
    assert a.status_code==b.status_code==200
    data=c.get('/api/workspace').json()
    assert len(data['customers'])==1 and len(data['jobs'])==2
    assert all(j['customer_id']==customer['id'] for j in data['jobs'])
    assert c.get('/api/customers?q=example%20lane').json()[0]['id']==customer['id']
    key=str(uuid.uuid4());payload={'customer_version':1,'service':'Retry test','message':'Invented retry test job'}
    first=write(c,'/api/customers/'+customer['id']+'/jobs',payload,key=key)
    assert write(c,'/api/customers/'+customer['id']+'/jobs',payload,key=key).json()==first.json()


def test_public_matching_remains_separate_even_same_contact(app):
    c=client(app);customer=contact(c)
    public=TestClient(app,headers=ORIGIN)
    r=write(public,'/api/public/oak-and-vale/enquiries',{'name':customer['name'],'email':customer['email'],'phone':customer['phone'],'address':customer['address'],'service':'New enquiry','message':'Identical contact still needs staff review.'})
    assert r.status_code==200
    assert c.get('/api/workspace').json()['jobs'][0]['customer_id']!=customer['id']
    assert len(c.get('/api/customers').json())==2
    assert write(public,'/api/public/oak-and-vale/enquiries',{'customer_id':customer['id'],'name':customer['name'],'service':'Bad match','message':'Must not choose a customer through public form.'}).status_code==422


def test_customer_history_financial_totals_and_immutable_names(app):
    c=client(app);j=job(c);q=quote(c,j);i=invoice(c,q)
    assert write(c,'/api/invoices/'+i['id']+'/payments',{'amount_pence':2500}).status_code==200
    history=c.get('/api/customers/'+j['customer_id']+'/history').json();cust=history['customer']
    changed=write(c,'/api/customers/'+cust['id'],{'version':1,'name':'Alex Corrected','email':'corrected@customer.example'},method='put')
    assert changed.status_code==200
    after=c.get('/api/customers/'+cust['id']+'/history').json()
    assert after['totals']=={'jobs':1,'invoiced_pence':10000,'received_pence':2500,'outstanding_pence':7500}
    assert after['quotes'][0]['original_customer_name']=='Alex Demo'
    assert after['invoices'][0]['original_customer_name']=='Alex Demo'
    assert c.get('/api/documents/invoice/'+i['id']).json()['snapshot']['customer']['name']=='Alex Demo'
    assert after['customer']['name']=='Alex Corrected' and len(after['contact_changes'])==1
    assert work(c,cust).status_code==409
    assert write(c,'/api/customers/'+cust['id'],{'version':1,'name':'Stale name'},method='put').status_code==409


def test_customer_edit_revokes_portal_and_reuse_exposes_only_selected_customer(app):
    c=client(app);j=job(c);p,_=portal(app,c,j);other=job(c,name='Alex Demo')
    customer=c.get('/api/customers/'+j['customer_id']+'/history').json()['customer']
    assert work(c,customer).status_code==200
    view=p.get('/api/portal/oak-and-vale/me').json()
    assert len(view['jobs'])==2
    assert write(c,'/api/customers/'+customer['id'],{'version':1,'name':'Alex Edited'},method='put').status_code==200
    assert p.get('/api/portal/oak-and-vale/me').status_code==401
    assert c.get('/api/customers/'+other['customer_id']+'/history').json()['totals']['jobs']==1


def test_customer_routes_business_csrf_portal_and_unknown_fields(app):
    c=client(app);b=client(app,1);j=job(c);cust=contact(c);p,_=portal(app,c,j)
    for other in (p,TestClient(app,headers=ORIGIN)):
        assert other.get('/api/customers').status_code==401
        assert other.get('/api/customers/'+cust['id']+'/history').status_code==401
        assert work(other,cust).status_code==401
    assert b.get('/api/customers/'+cust['id']+'/history').status_code==404
    assert work(b,cust).status_code==404
    assert write(b,'/api/customers/'+cust['id'],{'version':1,'name':'Wrong business'},method='put').status_code==404
    assert write(c,'/api/customers',{'name':'Spoof','business_id':'harbour'}).status_code==422
    c.headers['x-csrf-token']=''
    assert work(c,cust).status_code==403


def test_legacy_unversioned_customer_link_is_retired_without_changes(app):
    c=client(app);j=job(c);target=contact(c,'Existing Demo')
    from test_recovery import table_rows
    before=table_rows(app.state.store.root,'oak')
    payload={'job_version':1,'customer_id':target['id'],'customer_version':1}
    for suffix,body in [('customer-link-preview',payload),('customer-link',{**payload,'confirmed':True})]:
        r=write(c,'/api/jobs/'+j['id']+'/'+suffix,body)
        assert r.status_code==410 and 'review' in r.json()['detail'].lower()
    assert table_rows(app.state.store.root,'oak')==before


def test_new_exact_review_covers_bare_enquiry_with_reason_and_retry(app):
    from test_customer_reviews import selection,preview,confirm
    c=client(app);j=job(c);target=contact(c,'Existing Demo')
    plan=preview(c,selection(c,j,{'customer_id':target['id']},kind='jobs',job_ids=[j['id']]))
    assert plan['can_confirm']
    assert confirm(c,plan,confirmed=False).status_code==422
    assert confirm(c,plan,reason='').status_code==422
    key=str(uuid.uuid4());r=confirm(c,plan,key);assert r.status_code==200
    assert confirm(c,plan,key).json()==r.json()
    history=c.get('/api/customers/'+target['id']+'/history').json()
    assert history['jobs'][0]['id']==j['id'] and len(history['links'])==1
    assert len(c.get('/api/customers').json())==2
