"""
Utility functions to support entity data testing
"""

from __future__ import unicode_literals
from __future__ import absolute_import, division, print_function

__author__      = "Graham Klyne (GK@ACM.ORG)"
__copyright__   = "Copyright 2014, G. Klyne"
__license__     = "MIT (http://opensource.org/licenses/MIT)"

import logging
log = logging.getLogger(__name__)

import os
import copy
from collections import OrderedDict

from django.conf                import settings
from django.http                import QueryDict
from django.urls                import resolve, reverse

from utils.py3porting           import urljoin

from annalist.util              import valid_id
from annalist.identifiers       import RDF, RDFS, ANNAL
from annalist                   import layout

from annalist.views.form_utils.fieldchoice  import FieldChoice
from annalist.views.fields.render_placement import (
    get_placement_classes
    )

from .tests import (
    TestHost, TestHostUri, TestBasePath, TestBaseUri, TestBaseDir
    )
from .entity_testutils import (
    collection_dir, 
    site_title, 
    collection_entity_view_url,
    context_field_row
    )
from .entity_testfielddesc       import get_field_description, get_bound_field
from .entity_testentitydata      import entitydata_list_type_url

#   -----------------------------------------------------------------------------
#
#   Directory generating functions
#
#   -----------------------------------------------------------------------------

def recordview_dir(coll_id="testcoll", view_id="testview"):
    return collection_dir(coll_id) + layout.COLL_VIEW_PATH%{'id': view_id} + "/"

#   -----------------------------------------------------------------------------
#
#   URI generating functions
#
#   -----------------------------------------------------------------------------

def recordview_coll_url(site, coll_id="testcoll", view_id="testview"):
    return urljoin(
        site._entityurl,
        layout.SITE_COLL_PATH%{'id': coll_id} + "/" + 
        layout.COLL_VIEW_PATH%{'id': view_id} + "/"
        )

def recordview_url(coll_id, view_id):
    """
    URI for record view description data; also view using default entity view
    """
    if not valid_id(view_id):
        view_id = "___"
    return collection_entity_view_url(coll_id=coll_id, type_id="_view", entity_id=view_id)

def recordview_edit_url(action=None, coll_id=None, view_id=None):
    """
    URI for record view description editing view
    """
    viewname = ( 
        'AnnalistEntityDataView'        if action == "view"   else
        'AnnalistEntityNewView'         if action == "new"    else
        'AnnalistEntityEditView'        if action == "copy"   else
        'AnnalistEntityEditView'        if action == "edit"   else
        'AnnalistRecordViewDeleteView'  if action == "delete" else
        'unknown'
        )
    kwargs = {'coll_id': coll_id}
    if action != "delete":
        kwargs.update({'action': action, 'type_id': "_view", 'view_id': "View_view"})
    if view_id:
        if valid_id(view_id):
            kwargs.update({'entity_id': view_id})
        else:
            kwargs.update({'entity_id': "___"})
    return reverse(viewname, kwargs=kwargs)

#   -----------------------------------------------------------------------------
#
#   ----- RecordView data for default view
#
#   -----------------------------------------------------------------------------

def recordview_value_keys(view_uri=False, view_entity_type=True):
    keys = set(
        [ 'annal:id', 'annal:type_id'
        , 'annal:type'
        , 'annal:url'
        , 'rdfs:label', 'rdfs:comment'
        , 'annal:open_view'
        , 'annal:view_fields'
        ])
    if view_uri:
        keys.add('annal:uri')
    if view_entity_type or (view_entity_type == ""):
        keys.add('annal:view_entity_type')
    return keys

def recordview_load_keys(view_uri=False, view_entity_type=True):
    return (
        recordview_value_keys(view_uri=view_uri, view_entity_type=view_entity_type) | 
        {"@id", '@type', '@context'}
        )

def recordview_create_values(
        coll_id="testcoll", view_id="testview", update="RecordView", view_uri=None, 
        view_entity_type="annal:Test_default",
        num_fields=4, field3_placement="small:0,12",
        extra_field=None, extra_field_uri=None
        ):
    """
    Entity values used when creating a record view entity
    """
    view_values = (
        { 'annal:type':         "annal:View"
        , 'rdfs:label':         "%s %s/%s"%(update, coll_id, view_id)
        , 'rdfs:comment':       "%s help for %s in collection %s"%(update, view_id, coll_id)
        , 'annal:view_entity_type':  view_entity_type
        , 'annal:open_view':    True
        , 'annal:view_fields':
          [ { 'annal:field_id':         layout.FIELD_TYPEID+"/Entity_id"
            , 'annal:field_placement':  "small:0,12;medium:0,6"
            }
          , { 'annal:field_id':         layout.FIELD_TYPEID+"/Entity_type"
            , 'annal:field_placement':  "small:0,12;medium:6,6"
            }
          , { 'annal:field_id':         layout.FIELD_TYPEID+"/Entity_label"
            , 'annal:field_placement':  "small:0,12"
            }
          , { 'annal:field_id':         layout.FIELD_TYPEID+"/Entity_comment"
            # , 'annal:field_placement':  field3_placement
            }
          ]
        })
    if view_uri:
        view_values['annal:uri'] = view_uri
    if field3_placement:
        view_values['annal:view_fields'][3]['annal:field_placement'] = field3_placement
    if extra_field:
        efd = (
            { 'annal:field_id':         extra_field
            , 'annal:field_placement':  "small:0,12"
            })
        if extra_field_uri:
            efd['annal:property_uri'] = extra_field_uri
        view_values['annal:view_fields'].append(efd)
    if num_fields == 0:
        view_values['annal:view_fields'] = []
    return view_values

def recordview_values(
        coll_id="testcoll", view_id="testtype", view_uri=None, 
        update="RecordView", hosturi=TestHostUri, 
        view_entity_type="annal:Test_default",
        num_fields=4, field3_placement="small:0,12",
        extra_field=None, extra_field_uri=None):
    d = recordview_create_values(
        coll_id, view_id, update=update, view_uri=view_uri,
        view_entity_type=view_entity_type,
        num_fields=num_fields, field3_placement=field3_placement,
        extra_field=extra_field, extra_field_uri=extra_field_uri
        ).copy()
    view_url = recordview_url(coll_id, view_id)
    d.update(
        { 'annal:id':       view_id
        , 'annal:type_id':  "_view"
        , 'annal:url':      view_url
        })
    return d

def recordview_read_values(
        coll_id="testcoll", view_id="testview", view_uri=None, 
        view_entity_type="annal:Test_default",
        update="RecordView", hosturi=TestHostUri):
    d = recordview_values(
        coll_id, view_id, view_uri=view_uri, 
        view_entity_type=view_entity_type,
        update=update, hosturi=hosturi
        ).copy()
    d.update(
        { '@id':            layout.COLL_BASE_VIEW_REF%{'id': view_id}
        , '@type':          ["annal:View"]
        , '@context':       [{"@base": "../../"}, "../../coll_context.jsonld"]
        })
    return d

def recordview_values_add_field(view_data, 
    field_id="Entity_comment", 
    field_property_uri="rdfs:comment",
    field_placement="small:0,12"
    ):
    view_data['annal:view_fields'].append(
        { 'annal:field_id':         field_id
        , 'annal:property_uri':     field_property_uri
        , 'annal:field_placement':  field_placement
        })
    return view_data

#   -----------------------------------------------------------------------------
#
#   ----- Data in recordview view for default entity data
#
#   -----------------------------------------------------------------------------

default_view_fields_list = (
    [ { ANNAL.CURIE.field_id:           layout.FIELD_TYPEID+"/Entity_id"
      # , ANNAL.CURIE.property_uri:       ANNAL.CURIE.id
      , ANNAL.CURIE.field_placement:    "small:0,12;medium:0,6"
      }
    , { ANNAL.CURIE.field_id:           layout.FIELD_TYPEID+"/Entity_type"
      # , ANNAL.CURIE.property_uri:       ANNAL.CURIE.type_id
      , ANNAL.CURIE.field_placement:    "small:0,12;medium:6,6"
      }
    , { ANNAL.CURIE.field_id:           layout.FIELD_TYPEID+"/Entity_label"
      # , ANNAL.CURIE.property_uri:       RDFS.CURIE.label
      , ANNAL.CURIE.field_placement:    "small:0,12"
      }
    , { ANNAL.CURIE.field_id:           layout.FIELD_TYPEID+"/Entity_comment"
      # , ANNAL.CURIE.property_uri:       RDFS.CURIE.comment
      , ANNAL.CURIE.field_placement:    "small:0,12"
      }
    ])

view_view_fields_list = (
    [
      { ANNAL.CURIE.field_id:           layout.FIELD_TYPEID+'/View_id'
      , ANNAL.CURIE.property_uri:       ANNAL.CURIE.id
      , ANNAL.CURIE.field_placement:    "small:0,12;medium:0,6"
      }
    , { ANNAL.CURIE.field_id:           layout.FIELD_TYPEID+"/View_label"
      , ANNAL.CURIE.property_uri:       RDFS.CURIE.label
      , ANNAL.CURIE.field_placement:    "small:0,12"
      }
    , { ANNAL.CURIE.field_id:           layout.FIELD_TYPEID+"/View_comment"
      , ANNAL.CURIE.property_uri:       RDFS.CURIE.comment
      , ANNAL.CURIE.field_placement:    "small:0,12"
      }
    , { ANNAL.CURIE.field_id:           layout.FIELD_TYPEID+"/View_entity_type"
      , ANNAL.CURIE.property_uri:       ANNAL.CURIE.view_entity_type
      , ANNAL.CURIE.field_placement:    "small:0,12"
      }
    , { ANNAL.CURIE.field_id:           layout.FIELD_TYPEID+"/View_edit_view"
      , ANNAL.CURIE.property_uri:       ANNAL.CURIE.open_view
      , ANNAL.CURIE.field_placement:    "small:0,12;medium:0,6"
      }
    , { ANNAL.CURIE.field_id:           layout.FIELD_TYPEID+"/View_fields"
      , ANNAL.CURIE.property_uri:       ANNAL.CURIE.view_fields
      , ANNAL.CURIE.field_placement:    "small:0,12"
      }
    ])

# Field list within a view_fields field of a view display
# (this is confusingly, but unavoidably, self-referential)
group_field_list = (
    [ { ANNAL.CURIE.field_id:           "_field/View_field_sel"
      , ANNAL.CURIE.property_uri:       ANNAL.CURIE.field_id
      , ANNAL.CURIE.field_placement:    "small:0,12;medium:0,4"
      }
    , { ANNAL.CURIE.field_id:           "_field/View_field_property"
      , ANNAL.CURIE.property_uri:       ANNAL.CURIE.property_uri
      , ANNAL.CURIE.field_placement:    "small:0,12;medium:4,4"
      }
    , { ANNAL.CURIE.field_id:           "_field/View_field_placement"
      , ANNAL.CURIE.property_uri:       ANNAL.CURIE.field_placement
      , ANNAL.CURIE.field_placement:    "small:0,12;medium:8,4"
      }
    ])

def view_view_context_data(
        coll_id="testcoll", view_id="", orig_id=None,
        action=None, 
        view_uri=None,
        view_label=None,
        view_descr=None,
        view_entity_type="annal:Default_type",
        view_edit_view=True,
        view_fields=None, 
        add_field=None, remove_field=None, move_up=None, move_down=None,
        field_choices=None,
        update="RecordView",
        continuation_url=None
    ):
    if view_label is None:
        #@@TODO: use same format for both; change form data too
        if view_id:
            view_label = "%s %s/%s"%(update, coll_id, view_id)
        else:
            view_label = "%s data ... (%s/)"%(update, coll_id)
    # Target record fields listed in the view description
    if view_fields is None:
        view_fields = default_view_fields_list
    if continuation_url is None:
        continuation_url = entitydata_list_type_url(coll_id, layout.VIEW_TYPEID)
    view_heading = "View definition"
    view_title   = (
        "%s - %s - Collection %s"%(view_label, view_heading, coll_id) if view_label
        else
        "%s - Collection %s"%(view_heading, coll_id)
        )
    view_fields = copy.deepcopy(view_fields) # Prevents testing Heisenbugs
    bound_view_fields = get_bound_field("View_fields", view_fields)
    if field_choices is not None:
        # Add field choices to field definitions...
        bound_view_fields['description']['group_field_list'] = group_field_list
        group_field_descs = (
            [ get_field_description(f[ANNAL.CURIE.field_id]) 
              for f in group_field_list 
            ])
        fcs = OrderedDict()
        for fid in field_choices:
            fref      = layout.FIELD_TYPEID + "/" + fid
            fdesc     = get_field_description(fid)
            fc        = FieldChoice(fref, 
                label=fdesc['field_label'],
                link=TestBasePath+"/c/"+coll_id+"/d/"+fref+"/"
                )
            fcs[fref] = fc
        group_field_descs[0]['field_choices'] = fcs
        bound_view_fields['description']['group_field_descs'] = group_field_descs
    context_dict = (
        { 'title':              view_title
        , 'heading':            view_heading
        , 'coll_id':            coll_id
        , 'type_id':            layout.VIEW_TYPEID
        , 'orig_id':            orig_id
        , 'view_id':            'View_view'
        , 'entity_id':          view_id or ""
        , 'orig_id':            orig_id
        , 'orig_type':          layout.VIEW_TYPEID
        , 'record_type':        "annal:View"
        , 'continuation_url':   continuation_url
        , 'fields':
          [ context_field_row(
              get_bound_field("View_id",           view_id),                # 0 (0,0)
              )
          , context_field_row(
              get_bound_field("View_label",        view_label)              # 1 (1,0)
              )
          , context_field_row(
              get_bound_field("View_comment",      view_descr)              # 2 (2,0)
              )
          , context_field_row(
              get_bound_field("View_entity_type",  view_entity_type)        # 3 (3,0)
              )
          , context_field_row(
              get_bound_field("View_edit_view",    view_edit_view)          # 4 (4,0)
              )
          , bound_view_fields  # 5 (5, 0)
          ]
        })
    if view_uri:
        context_dict['entity_uri']  = view_uri
    if action:  
        context_dict['action']      = action
    if add_field:
        context_dict['fields'][5]['field_value'].append(
            { 'annal:field_id':         None
            , 'annal:property_uri':     None
            , 'annal:field_placement':  None
            })
    if remove_field:
        context_dict['fields'][5]['field_value'][3:4] = []
    if move_up:
        assert move_up == [2,3]
        fields = context_dict['fields'][5]['field_value']
        context_dict['fields'][5]['field_value'] = [ fields[i]  for i in [0,2,3,1] ]
    if move_down:
        assert move_down == [1]
        fields = context_dict['fields'][5]['field_value']
        context_dict['fields'][5]['field_value'] = [ fields[i]  for i in [0,2,1,3] ]
    return context_dict

def view_view_form_data(
        coll_id="testcoll", orig_coll=None,
        view_id="", orig_id=None, 
        action=None, cancel=None, 
        view_entity_type="annal:View",
        field3_placement="small:0,12",
        extra_field=None,       # Extra field id for some tests (e.g. dup property uri)
        extra_field_uri=None,   # Extra field property URI to add in
        add_field=None,         # True for add field option
        remove_fields=None,     # List of field numbers to remove (as strings)
        move_up_fields=None,    # List of field numbers to move up
        move_down_fields=None,  # List of field numbers to move down
        new_enum=None,          # Id of new-value button enumerated value field
        update="RecordView"
        ):
    form_data_dict = (
        { 'View_label':         '%s data ... (%s/%s)'%(update, coll_id, view_id)
        , 'View_comment':       '%s description ... (%s/%s)'%(update, coll_id, view_id)
        , 'View_entity_type':   view_entity_type
        , 'View_edit_view':     "Yes"
        , 'orig_id':            'orig_view_id'
        , 'record_type':        'annal:View'
        , 'continuation_url':   entitydata_list_type_url(coll_id, "_view")
        # View fields
        , 'View_fields__0__View_field_sel':          layout.FIELD_TYPEID+"/Entity_id"
        , 'View_fields__0__View_field_placement':    "small:0,12;medium:0,6"
        , 'View_fields__1__View_field_sel':          layout.FIELD_TYPEID+"/Entity_type"
        , 'View_fields__1__View_field_placement':    "small:0,12;medium:6,6"
        , 'View_fields__2__View_field_sel':          layout.FIELD_TYPEID+"/Entity_label"
        , 'View_fields__2__View_field_property':     "rdfs:label"
        , 'View_fields__2__View_field_placement':    "small:0,12"
        , 'View_fields__3__View_field_sel':          layout.FIELD_TYPEID+"/Entity_comment"
        , 'View_fields__3__View_field_property':     "rdfs:comment"
        , 'View_fields__3__View_field_placement':    field3_placement
        })
    if extra_field:
        # Insert extra field with supplied Id
        form_data_dict['View_fields__4__View_field_sel']       = layout.FIELD_TYPEID+"/"+extra_field
        form_data_dict['View_fields__4__View_field_placement'] = "small:0,12"
        if extra_field_uri:
            form_data_dict['View_fields__4__View_field_property'] = extra_field_uri
    if view_id is not None:
        form_data_dict['entity_id']     = view_id
    if view_id:
        form_data_dict['entity_id']     = view_id
        form_data_dict['View_label']    = '%s %s/%s'%(update, coll_id, view_id)
        form_data_dict['View_comment']  = '%s help for %s in collection %s'%(update, view_id, coll_id)
        form_data_dict['View_uri']      = TestBaseUri + "/c/%s/d/_view/%s/"%(coll_id, view_id)
        form_data_dict['orig_id']       = view_id
        form_data_dict['orig_type']     = "_view"
        form_data_dict['orig_coll']     = coll_id
    if orig_id:
        form_data_dict['orig_id']       = orig_id
    if orig_coll:
        form_data_dict['orig_coll']     = orig_coll
    if action:
        form_data_dict['action']        = action
    if cancel:
        form_data_dict['cancel']        = "Cancel"
    elif add_field:
        form_data_dict['View_fields__add'] = "Add field"
    elif remove_fields:
        form_data_dict['View_fields__remove'] = "Remove field"
        if remove_fields != "no-selection":
            form_data_dict['View_fields__select_fields'] = remove_fields
    elif move_up_fields:
        form_data_dict['View_fields__up'] = "Move up"
        if move_up_fields != "no-selection":
            form_data_dict['View_fields__select_fields'] = move_up_fields
    elif move_down_fields:
        form_data_dict['View_fields__down'] = "Move down"
        if move_down_fields != "no-selection":
            form_data_dict['View_fields__select_fields'] = move_down_fields
    elif new_enum:
        form_data_dict[new_enum]        = new_enum
    else:
        form_data_dict['save']          = 'Save'
    return form_data_dict

#   -----------------------------------------------------------------------------
#
#   ----- Recordview delete confirmation form data
#
#   -----------------------------------------------------------------------------

def recordview_delete_confirm_form_data(view_id=None):
    return (
        { 'viewlist':    view_id,
          'view_delete': 'Delete'
        })

# End.
